子进程死亡会给父进程发送 SIGCHLD 信号!(默认动作:父进程忽略该信号)此时父进程就可以捕捉该信号,利用回调函数使用 waitpid 回收子进程。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
void catch_sig(int sig) // 参数:是捕捉的信号编号
{
pid_t pid;
while(1)
{
pid = waitpid(-1, NULL, WNOHANG);
if(pid <= 0)
{
break;
}
printf("died child_id=%d\n",pid);
} }
int main()
{
// 问题:父进程注册捕捉的时候,子进程已经死亡,这样信号根本捕捉不到
// 重要思想:先屏蔽信号,再解除屏蔽
// 这样:即使子进程先死,内核产生的信号也会被屏蔽掉,然后再解除屏蔽,信号就可以被顺利捕捉了
// 父进程先设置屏蔽信号集,屏蔽SIGCHLD信号
sigset_t proc; // 定义自定义信号集
sigemptyset(&proc); // 第一步:清空自定义信号集,防止随机数干扰其他信号
sigaddset(&proc, SIGCHLD); // 将 sigchld 信号 添加到自定义集合
sigprocmask(SIG_BLOCK, &proc, NULL); // 通过 自定义信号集 设置 屏蔽信号集
// 创建5个子进程
int i = 0;
for(i = 0 ;i < 5; i ++)
{
pid_t pid = fork();
if(pid == 0){
break;
}
}
// 此时,6个进程在运行
if(i < 5){
// 子进程
printf("I am %d child, mypid=%d\n", i+1, getpid());
}
else if(i == 5){
// 父进程逻辑 前期已经将信号屏蔽掉了,此时使用 sigaction 进行捕捉
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = catch_sig;
sigemptyset(&act.sa_mask); // 不设置临时屏蔽信号
sigaction(SIGCHLD, &act, NULL);
// 注意:先注册捕捉,再解除屏蔽,顺序不能反
sigprocmask(SIG_UNBLOCK, &proc, NULL);
while(1){
sleep(1); // 父进程一直存活,使子进程先执行return死亡
}
}
return 0;
}