子进程执行结束之后,父进程如果不对其进行回收,子进程就会变为僵尸进程。
父进程可以通过调用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函数执行多次,才可以将之前死掉的所有子进程回收。