上海至河内机票:《UNIX环境高级编程》的笔记——wait&waitpid

来源:百度文库 编辑:偶看新闻 时间:2024/06/12 01:13:03
wait&waitpid 进程结束,无论是否正常中止,内核都会给它的父进程发送一个SIGCHILD的通知。(虽然这个老爸一点都不计划生育也不在乎孩子,但是孩子死了总得通知他一下吧)。显然这个通知是异步的,某天老爸下班回家可能就在邮箱里看到它若干孩子的死亡证明了。父进程是比较不负责任的,所以它可能随手把这个死亡证明扔了(ignore it),或者如果他比较爱那个孩子的话,就给它办个葬礼(signal handler)。呐,都说了这个老爸是不负责任的,所以default方式就是扔了。 作为一个进程,它调用wait或者waitpid的话会 1.block。 如果它所有的子进程都在运行(这里是否应该理解为这些子进程没有死掉) 2.带着某个子进程的中止状态立即返回。如果有一个子进程已经中止并等待它父进程来存取它的中止状态。 3,如果它已经没有子进程了,就以错误退出。 wait会使调用者block,直到它有某个子进程中止为止。而waitpid的第三个参数可使调用者不中止。 1、下面部分来自:http://haipo.me/blog/65
第八章进程控制,其中讲到了僵死进程,它的定义是这样的:
        在 UNIX 术语中,一个已经终止,但其父进程尚未对其进行善后处理(获取终止进程的有关信息,释放它仍占 有 的资源)的进程被称为僵死进程( zombie )。

如果编写一个长期运行的程序,它调用 fork 产生了很多子进程,那么除非父进程等待取得子进程的终止状态,否则这些子进程终止后就会变成僵死进程。

我创建了一个子进程来做这些事情,和父进程并行运行,从而不引起服务中断。代码类似于:

... if ((pid = fork()) == 0) {     /* do some thing */      exit(0); } ...

这样子进程调用 exit() 进入终止状态后,父进程没有调用 wait() / waitpid() 函数,也没有显式忽略 SIGCHLD 信号,进程就会进入僵死状态。我在程序运行一段时间后,使用命令

ps -ef | grep a.out

果然发现了很多僵死进程。僵死进程消耗很小的计算机资源,但是 Linux 系统对运行的进程数量有限制,如果产生过多的僵尸进程占用了可用的进程号,将会导致新的进程无法生成。但程序如果使用 wait() / waitpod() 阻塞等待的话,已经失去创建子进程的意义。

解决办法一:程序在进入终止状态时会向父进程发送 SIGCHLD 信号,在 linux 上,如果在程序开始运行时显式忽略该信号可以避免僵死进程的产生,代码如下:

if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {     fputs("Cannot catch SIGCHLD\n", stderr);     exit(1); }

这样做并不是一个标准的做法,因为在某些 UNIX 操作系统上,忽略该信号并不能避免僵死进程的产生。

解决办法二:更好的做法是调用 signal() 函数接管 SIGCHLD 信号,父进程收到此信号后,执行 waitpid() 函数为子进程收尸。代码如下:

static void process_wait(int sig) {     while (waitpid(-1, NULL, WNOHANG) > 0)         ; } ... if (signal(SIGCHLD, process_wait) == SIG_ERR) {     fputs("Cannot catch SIGCHLD\n", stderr);     exit(1); } ...waitpid() 函数在指定 WNOHANG 参数时,并不会阻塞进程运行。此时 waitpid() 作用为获得进程的运行信息,当子进程没有结束时返回值为 0, 在已经结束则返回值为子进程 ID. 2、下面的部分是转来的,源网址http://doc.linuxpk.com/53457.html
查看linux源代码 unistd.h 我们会发现,其实 wait 就是经过包装的 waitpid:staticinlinepid_twait(int*wait_stat){
    returnwaitpid(-1,wait_stat,0);}
waitpid的返回值比wait稍微复杂一些,一共有3种情况:1. 当正常返回的时候,waitpid返回收集到的子进程的进程ID;2. 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;3. 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD;下面看一个简单的例子:下载:waitpid.c/* waitpid.c */#include#include#include#includeint main(){
   pid_t pc,pr;   pc=fork();   if (pc<0)/* fork错误*/   {       printf("fork error\n");       exit(1);   }   else if(pc==0)/*在子进程中*/   {       sleep(10);       exit(0);   }   else   {       do {/* 使用了WNOHANG参数,waitpid不会在这里等待 */           pr=waitpid(pc,NULL,WNOHANG);           if (pr==0)           {               printf("No child exit\n");               sleep(1);           }          }while (pr==0);       if (pr==pc)           printf("successfully get child %d\n",pr);       else           printf("wait child error\n");   }   return 0;}
编译并运行:$ gcc -o waitpid waitpid.c$ ./waitpidNo child exitNo child exitNo child exitNo child exitNo child exitNo child exitNo child exitNo child exitNo child exitNo child exitsuccessfully get child 4607父进程经过10次失败的尝试之后,终于收集到了退出的子进程。父进程和子进程分别睡眠了10秒钟和1秒钟,代表它们分别作了10秒钟和1秒钟的工作。父子进程都有工作要做,父进程利用工作的简短间歇察看子进程的是否退出,如退出就收集它。