snifer

[原创]嵌入式中fork、exit和exec系统调用编写多进程程序

0
阅读(3368)

假期过得太快了,一眨眼就要开始上班了,假期帮一个出版社写了几个教程,贴出来与大家分享一下。


系统调用的程序在嵌入式系统开发中非常重要,编写相关程序加深对系统进程及其控制的了理解。

基本原理和方法:

fork后父子进程会同步运行,但父子进程的返回顺序是不确定的。设两个变量global和test来检测父子进程共享资源的情况。同时在进程退出时对exit和_exit的区别进行测试和说明。

1.fork

#include <stdio.h>

#include <sys/types.h>

#include <sys/wait.h>

#include <unistd.h>

#include <stdlib.h>

#include <fcntl.h>

//#include <sys/exits.h>

int     global=22;

char  buf[]="the test content!\n";

 

int main(void)

{        int test=0,stat;

          pid_t pid;

          if(write(STDOUT_FILENO,buf,sizeof(buf)-1)!=sizeof(buf)-1)

                   perror("write error!");

          printf(" fork test!\n");

/* fork */

          pid = fork();   /*we should check the error*/

          if(pid == -1){

                   perror("fork");

                   exit;  }

/*if the pid=0 then it is the child*/

          else if(pid == 0){

          global++;   test++;

          printf("global=%d test%d Child,my PID is %d\n",global,test,getpid());

          exit(0);

}

/*else be the parent*/

          global+=2;

          test+=2;

          printf("global=%d test%d Parent,my PID is %d\n",global,test,getpid());

          exit(0);

          //printf("global=%d test%d Parent,my PID is %d",global,test,getpid());

          //_exit(0);

}

编译执行,并分析结果:

          [root@localhost root]# ./test

the test content!

 fork test!

global=23 test=1 Child,my PID is 2751

global=24 test=2 Parent,my PID is 2750

可以看出父子进程打印出了各自的进程号和对应变量的值,显然global和test在父子进程间是独立的,其各自的操作不会对对方的值有影响。将上述代码最后的两行代码替换为注释掉的_exit(0)行,重新编译,查看结果,解释原因:

[root@localhost root]# ./test

the test content!

 fork test!

global=23 test=1 Child,my PID is 2771

父进程的信息没有打印出来,其原因是:_exit()函数直接使进程停止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构;而exit()函数则在这些基础上作了一些包装,在执行退出之前加了若干道工序。exit()函数在调用exit系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件,即会 "清理I/O缓冲"。若将上述_exit(0)改为exit(0),则肯定会有打印。另外,需要注意换行符\n会引起IO的清理操作,若下面的语句printf("global=%d test%d Parent,my PID is %d",global,test,getpid()); 加上\n,则调用_exit(0)的结果和调用exit(0)的结果是一样的。

2.vfork的特点

         将上述代码的pid = fork();  改为pid = vfork();编译后运行结果如下:

[root@localhost root]# ./test

the test content!

 fork test!

global=23 test=1 Child,my PID is 2849

global=25 test=3 Parent,my PID is 2848

可以看出,vfork与fork区别在于共享的资源不一样,vfork调用后,子进程先对global和test加1,父进程运行时,在其基础之上再加2,得到上述运行结果。即vfork的特点是:在调用execv或者exit前子进程对变量的修改会影响到父进程,即他们是共享的;

特别注意:父进程等待子进程调用execv或exit才继续执行。则若子进程依赖父进程的进一步动作时,父进程又必须阻塞到子进程调用execv或者exit才会往下执行,此时就会造成“死锁”。读者可自己设计测试一下这种“死锁”状态。

3.execv函数族的使用

注意点:调用execv后,程序不再返回!在上述代码基础上,在子进程的退出代码前加入如下代码:

printf("global=%d test%d Child,my PID is %d\n",global,test,getpid());

if(execl("/bin/ps","ps","-au",NULL)<0)

                   perror("execl error!");

printf("this message will never be printed!\n");

exit(0);

编译运行后结果为:

[root@localhost root]# ./test

the test content!

 fork test!

global=23 test=1 Child,my PID is 2909

USER       PID %CPU %MEM   VSZ  RSS TTY      STAT START   TIME COMMAND

root      2719  0.0  0.6  43601032 pts/1    S    23:14   0:00 /bin/bash

root      2908  0.0  0.1  1340 276 pts/1    R    23:38   0:00 ./test

root      2909  0.0  0.4  2684 736 pts/1    R    23:38   0:00 ps -au

global=25 test=3 Parent,my PID is 2908

4.waitpid的作用是等待子进程退出并回收其资源,同时可以通过WIFEXITED等宏调用可以检测子进程退出的状态。在第一个示例fork使用的代码基础上进行修改,添加检测进程退出状态的子函数,参考代码如下:

void exit_check(int stat)

{ if(WIFEXITED(stat))

          printf("exit normally!the return code is: %d \n",WEXITSTATUS(stat));

  else if(WIFSIGNALED(stat))

          printf("exit abnormally!the signal code is: %d %s\n",WTERMSIG(stat),

  #ifdef   WCOREDUMP /

          WCOREDUMP(stat) ? "(core file generated)":"");

  #else

          "");

  #endif    // 条件编译,如WIFSIGNALED(stat)为非0

                      //且此进程产生一个内存映射文件(core dump)则返回非0

  else if(WIFSTOPPED(stat))  //如果子进程暂停(stopped)则返回非0

          printf("!the stop code is: %d \n",WSTOPSIG(stat));

}

在父进程处理global和test变量前加入如下代码:

if(waitpid(pid,&stat,0)!=pid)

          perror("wait error");

exit_check(stat);  // the status of exit check

编译运行后结果为:

[root@localhost root]# ./test

the test content!

 fork test!

global=23 test=1 Child,my PID is 2973

exit normally!the return code is: 0

global=24 test=2 Parent,my PID is 2972

可以看出父进程回收了退出的子进程的资源,检测到了它的退出状态。

相信大家对系统调用程序有了深刻认识,有问题的朋友可以给我发消息,大家一起探讨。