子进程的异步等待方式

      

      我们知道当一个父进程创建一个子进程时,最好要调用wait或者waitpid函数等待子进程,不然会产生僵尸进程造

成内存泄漏的问题。一般父进程在等待子进程时有两种方式等待,一种是阻塞式等待,这时父进程不能处理自己的其

他事情;另一种是非阻塞式等待,父进程处理自己的工作的同时,要以定时轮询的方式,去查看有没有子进程等待清

理。

     其实,子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号的处理函数,这样父进程只专心处理自己的工作,不必关心子进程,子进程终止会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。


1、 wait()和waitpid()的区别

<1>wait 函数:用来等待任何一个子进程退出,由父进程调用。

#include<sys/types.h>
#include<sys/wait.h> 
pid_t wait(int* status);

返回值 
成功返回被等待子进程的pid,失败返回-1。 status参数 : 输出型参数,拿回子进程的退出信息。 
wait函数 : 阻塞式调用,等待的子进程不退出时,父进程一直不退出。 
目的: 回收子进程,系统回收子进程的空间。
参数status 
如果不为空,则进程终止状态被保存于其中。
WIFEXITED :正常返回时为真,可以执行宏函数 WEXITSTATUS获取子进程传送给exit、_exit或_Exit的参数的低8位。 
WIFSIGNALED :异常返回时为真,可以执行宏函数WTERMSIG取得子进程终止的信号编号,另外,对于一些实现,定义有宏WCOREDUMP宏,若以经昌盛终止进程的core文件,则为真。 
WIFSTOPPED : 若为当前暂停子进程的返回的状态,则为真,可执行WSTOPSIG取得使子进程暂停的信号编号。 
WIFCONTINUED : 若在作业控制暂停后已经继续的子进程返回了状态,则为真,仅用于waitpid。

<2>waitpid函数

#include<sys/types.h>
 #include<sys/wait.h>
 pid_t waitpid(pid_t pid,int* status,int options); 

pid参数 
从参数的名字pid和类型pid_t中就可以看出,这里需要的是一个进程ID。但当pid取不同的值时,在这里有不同的意义。 
1)pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。 
2)pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。 
3)pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。 
4)pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。

status参数 
与wait()函数的基本相同,此处不再赘述。

options参数 
当options参数为0时,waitpid函数 与wait函数功能相同,仍是阻塞式等待,不提供额外功能,如果为下列常量按位或则提供更多功能: 
WCONTINUED:若实现支持作业控制,那么由pid指定的任一子进程在暂停后已经继续,但状态尚未报告,则返回状态 ;
WNOHANG:若由pid指定的子进程并不是立即可用的,则waitpid不阻塞,即此时以非阻塞方式(轮询式访问的必要条件)等待子进程,并且返回0; 
WUNTRACED:若实现支持作业控制,而pid指定的任一子进程已经暂停,且其状态尚未报告,则返回其状态。

返回值 
当正常返回的时候,waitpid返回收集到的子进程的进程ID; 
如果设置了选项WNOHANG,而调用中waitpid发现此时没有已退出的子进程可收集,则返回0; 
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在; 
当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD;

waitpid提供了三个wait所没有的功能: 
1)waitpid可等待一个特定的进程 
2) waitpid提供了一个wait的非阻塞版本 
3) waitpid支持作业控制

下面验证子进程退出时给父进程发送信号的机制
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
void catchsig(int sig)
{
   printf("get a sig:%d,pid:%d\n",sig,getpid());
}
int main()
{
   signal(SIGCHLD,catchsig);
   pid_t id=fork();
   if(id==0)
   {//child
      printf("I am child,quit! pid:%d\n",getpid());
      exit(1);
   }
   else
   {//father
     waitpid(id,NULL,0);
   }
   return 0;
}

运行结果:



由结果可以看出:子进程退出时会给父进程发送17号信号,而这个17 号信号就是SIGCHLD信号。可以用kill -l 命令看一下。

父进程等待子进程的异步版本:
父进程自定义SIGCHLD信号的处理函数,并采用非阻塞方式等待,当子进程突出时,会向父进程发送信号,父进程会进行回收。
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
void catchsig(int sig)
{
   do
   {
      pid_t ret=waitpid(-1,NULL,WNOHANG);
      if(ret>0)
      {
         printf("wait success:%d\n",ret);
      }
      else
      {
         printf("wait filed:%d\n",ret);
         break;
      }
   }while(1);
}
int main()
{
   signal(SIGCHLD,catchsig);
   pid_t id=fork();
   if(id==0)
   {//child
      printf("I am child,quit! pid:%d\n",getpid());
      exit(1);
   }
   else
   {//father
      while(1)
      {
          printf("do father things!\n");
          sleep(1);
      }
   }
   return 0;
}

看运行结果:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值