利用SIGCHLD信号回收子进程

嵌入式linux系统开发 专栏收录该内容
4 篇文章 0 订阅

子进程执行结束之后,父进程如果不对其进行回收,子进程就会变为僵尸进程。
父进程可以通过调用wait()函数和waitpid()函数去回收子进程.
由于子进程结束时会发送SIGCHLD信号给父进程,不过此信号的默认动作为忽略,我们可以通过系统函数sigaction()设置信号捕捉,在信号捕捉函数中去回收子进程。
信号捕捉函数中回收子进程的方法:
waitpid函数

下面展示一些 代码片

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>

void cat_sigchild(int sig)
{
   int status;
   pid_t pid;

   while((pid =  waitpid(0,&status,WNOHANG)) > 0)
   {
    if(WIFEXITED(status))
    {
	printf("child exit with %d\n",WEXITSTATUS(status));
    }
    else if(WIFSIGNALED(status))
    {
	printf("child kill by sig %d\n",WTERMSIG(status));
    }
   }
}

int main(int argc,char *argv[])
{
    pid_t pid;
    int i;

    for(i=0;i<10;i++)
    {
        pid = fork();
 	    if(pid == 0)
		{
		break;
		}
		 else  if(pid <0)
		{
		 perror("fork:");
		 exit(1);
		}

    }

if(pid == 0)
{
  //if(i== 5)
  //sleep(i+1);

printf("i am %d child pid is %d\n",i+1,getpid());
return i+1;
}
else if(pid >0)
{
  int n = 10000000;
//  sleep(4);
  //阻塞SIGCHLD信号
  sigset_t newset,oldset;
  sigemptyset(&newset);
  sigaddset(&newset,SIGCHLD);
  sigprocmask(SIG_BLOCK,&newset,&oldset); 
  //如果在信号捕捉函数设置之前,子进程先结束,那么如果提前不阻塞SIGCHLD信号的话,就会导致先结束的子进程无法回收,所以在设置信号捕捉函数之前应该先阻塞SIGCHLD信号。
  while(n --)  //模拟程序执行,让子进程先结束。
  {}
  
  struct sigaction act,oldact;
  act.sa_handler = cat_sigchild;
  act.sa_flags = 0;
  sigemptyset(&act.sa_mask);
  sigaction(SIGCHLD,&act,NULL);
  
  //解除阻塞
  sigprocmask(SIG_SETMASK,&oldset,NULL);  

  while(1)
  {
    printf("parent id %d\n",getpid());
    sleep(1);
  }
}
return 0;
}

注意点:
这里需要注意的是:我们在设置SIGCHLD信号的信号捕捉函数之前为了程序的严谨性,要先使用系统函数sigprocmask()去阻塞SIGCHLD信号,在设置完SIGCHLD信号的信号捕捉函数之后再解除阻塞。
原因:
如果我们的子进程先于父进程执行,假如在父进程设置完SIGCHLD的信号捕捉函数之前所有子进程都执行结束了,那么父进程就不会再收到子进程发送的SIGCHLD信号,信号捕捉函数就不会执行,进而回收子进程的系统函数waitpid()就不会被调用,那么就会造成所有的子进程变为僵尸进程。

解决办法:

1:设置信号阻塞,在父进程设置完SIGCHLD信号的信号捕捉函数之前阻塞SIGCHLD信号。这样即使所有的子进程都结束了,由于信号处于阻塞状态,当我们设置完SIGCHLD信号的捕捉函数之后,解除阻塞,就可以正常捕获到子进程发送的SIGCHLD信号,就可以进入捕捉函数调用waitpid去回收 死去的子进程 的资源。
注意
这里想要回收所有子进程调用waitpid必须使用while循环结构,不能使用if结构。因为在执行SIGCHLD信号捕捉函数期间,如果两个或多个子进程同时结束,那么SIGCHLD信号只记录一次,此时如果使用if结构就会导致同时结束的子进程只回收一个。

2:我们可以人为的使用kill -17 (父进程号) 命令给父进程发送SIGCHLD信号,就会触发信号捕捉函数,在捕捉函数内部利用
while((pid = waitpid(0,&status,WNOHANG)) > 0)结构去回收所有死掉的子进程。
注意:在父进程之前死掉的子进程发送的SIGCHLD信号默认动作为忽略,所以可以将之前先于父进程死掉的子进程回收靠的并不是之前子进程的SIGCHLD信号,而是靠的后来收到的SIGCHLD信号,和waitpid函数.
在这种人为发送信号回收的情况下:
SIGCHLD信号捕捉函数只调用执行一次,waitpid函数执行多次,才可以将之前死掉的所有子进程回收。

  • 3
    点赞
  • 3
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值