产生SIGCHLD信号的条件
- 子进程结束的时候
- 子进程收到SIGSTOP信号
- 当子进程停止时,收到SIGCONT信号
SIGCHLD信号的作用
子进程退出后,内核会给它的父进程发送SIGCHLD信号,父进程收到这个信号后可以对子进程进行回收。
使用SIGCHLD信号完成对子进程的回收可以避免父进程阻塞等待而不能执行其他操作,只有当父进程收到SIGCHLD信号之后才去调用信号捕捉函数完成对子进程的回收,未收到SIGCHLD信号之前可以处理其他操作。
以一个例子引入:
父进程创建三个子进程,然后让父进程捕获SIGCHLD信号完成对子进程的回收。
程序中可能会存在下列问题
1. 有可能还未完成信号处理函数的注册三个子进程都退出了。
解决办法:可以在fork之前先将SIGCHLD信号阻塞,当完成信号处理函数的注册后在解除阻塞。
2. 当SIGCHLD信号函数处理期间, SIGCHLD信号若再次产生是被阻塞的,而且若产生了多次, 则该信号只会被处理一次, 这样可能会产生僵尸进程。
解决办法: 可以在信号处理函数里面使用while(1)循环回收, 这样就有可能出现捕获一次SIGCHLD信号但是回收了多个子进程的情况,从而可以避免产生僵尸进程。
测试代码如下:
//父进程使用SICCHLD信号完成对子进程的回收
#include<iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
using namespace std;
void waitchild(int signo)
{
pid_t wpid;
//回收子进程:用仅剩的一个信号循环回收
while(1)
{
wpid = waitpid(-1, NULL, WNOHANG);
if(wpid>0)
{
cout << "child is quit, wpid==" << wpid << endl;
}
else if(wpid==0)
{
cout << "child is living, wpid==" << wpid << endl;
break;
}
else if(wpid==-1)
{
cout << "no child is liviing, wpid==" << wpid << endl;
break;
}
}
}
int main()
{
int i = 0;
int n = 3;
//将SIGCHLD信号阻塞:防止还没有完成SIGCHLD信号的注册,三个子进程会全部退出,信号保留到未决信号集
//完成注册之后,解除阻塞,此时只会执行一个信号。随后在循环回收。
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, NULL);
for(i=0; i<n; i++)
{
//fork子进程
pid_t pid = fork();
if(pid<0) //fork失败的情况
{
perror("fork error");
return -1;
}
else if(pid>0) //父进程
{
cout << "father: pid==" << getpid() << " cpid==" << pid << endl;
sleep(1);
}
else if(pid==0) //子进程
{
cout << "child: pid==" << getpid() << " fpid==" << getppid()<< endl;
break;
}
}
//父进程
if(i==3)
{
cout << "the father: pid==" << getpid() << endl;
//signal(SIGCHLD, waitchild);
//注册信号处理函数
struct sigaction act;
act.sa_handler = waitchild;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sleep(5);
sigaction(SIGCHLD, &act, NULL);
//解除对SIGCHLD信号的阻塞
sigprocmask(SIG_UNBLOCK, &mask, NULL);
while(1)
{
sleep(1);
}
}
//第1个子进程
if(i==0)
{
cout << i << ":child: cpid==" << getpid() << endl;
//sleep(1);
}
//第2个子进程
if(i==1)
{
cout << i << ":child: cpid==" << getpid() << endl;
sleep(1);
}
//第3个子进程
if(i==2)
{
cout << i << ":child: cpid==" << getpid() << endl;
sleep(1);
}
return 0;
}
结果如下:上述情况下三个子进程都被回收 !