分享

《UNIX环境高级编程》的笔记——wait&waitpid

 幸福的乐土 2012-02-15
wait&waitpid
进程结束,无论是否正常中止,内核都会给它的父进程发送一个SIGCHILD的通知。(虽然这个老爸一点都不计划生育也不在乎孩子,但是孩子死了总得通知他一下吧)。显然这个通知是异步的,某天老爸下班回家可能就在邮箱里看到它若干孩子的死亡证明了。父进程是比较不负责任的,所以它可能随手把这个死亡证明扔了(ignore it),或者如果他比较爱那个孩子的话,就给它办个葬礼(signal handler)。呐,都说了这个老爸是不负责任的,所以default方式就是扔了。
作为一个进程,它调用wait或者waitpid的话会
1.block。 如果它所有的子进程都在运行(这里是否应该理解为这些子进程没有死掉)
2.带着某个子进程的中止状态立即返回。如果有一个子进程已经中止并等待它父进程来存取它的中止状态。
3,如果它已经没有子进程了,就以错误退出。
wait会使调用者block,直到它有某个子进程中止为止。而waitpid的第三个参数可使调用者不中止。
 
1、下面部分来自:http:///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./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<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdio.h>
int 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
$ ./waitpid
No child exit
No child exit
No child exit
No child exit
No child exit
No child exit
No child exit
No child exit
No child exit
No child exit
successfully get child 4607
父进程经过10次失败的尝试之后,终于收集到了退出的子进程。父进程和子进程分别睡眠了10秒钟和1秒钟,代表它们分别作了10秒钟和1秒钟的工作。父子进程都有工作要做,父进程利用工作的简短间歇察看子进程的是否退出,如退出就收集它。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多