SIGCHLD的产生条件
子进程终止时
子进程接收到SIGSTOP信号停止时
子进程处在停止态,接受到SIGCONT后唤醒时
借助SIGCHLD信号回收子进程
子进程结束运行,其父进程会收到SIGCHLD信号。该信号的默认处理动作是忽略。可以捕捉该信号,在捕捉函数中完成子进程状态的回收。
SIGCHLD信号注意问题
1.子进程继承了父进程的信号屏蔽字和信号处理动作,但子进程没有继承未决信号集。
2.注意注册信号捕捉函数的位置。
3.应该在fork之前,阻塞SIGCHLD信号。注册完捕捉函数后解除阻塞。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
void sys_err(char *str)
{
perror(str);
exit(1);
}
void do_sig_child(int signo)
{
int status;
pid_t pid;
// if ((pid = waitpid(0, &status, WNOHANG)) > 0) {
//这里不能用if而必须用while的原因在于:如果在执行该SIGCHLD信号捕捉函数时,有多个子进程又发出了SIGCHLD信号,由于阻塞信号不支持排队,那么多个信号只会被记录一次
while ((pid = waitpid(0, &status, WNOHANG)) > 0)
{
if (WIFEXITED(status))
printf("------------child %d exit %d\n", pid, WEXITSTATUS(status));
else if (WIFSIGNALED(status))
printf("child %d cancel signal %d\n", pid, WTERMSIG(status));
}
}
int main(void)
{
pid_t pid;
int i;
//在执行fork()前先阻塞SIGCHLD信号
sigset_t myset;
sigemptyset(&myset);
sigaddset(&myset,SIGCHLD);
sigprocmask(SIG_BLOCK,&myset,NULL);
for (i = 0; i < 10; i++) //循环创建10个子进程
{
if ((pid = fork()) == 0)
break;
else if (pid < 0)
sys_err("fork");
}
if (pid == 0)
{
printf("child ID %d\n", getpid());
sleep(1);
return i+1;
}
else if (pid > 0)
{
struct sigaction act;
act.sa_handler = do_sig_child;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGCHLD, &act, NULL);
//注册SIGCHLD信号捕捉函数后才能解除对SIGCHLD的阻塞,防止在SIGCHLD信号捕捉函数注册前就有SIGCHLD发出的情况
sigprocmask(SIG_UNBLOCK,&myset,NULL);
while (1)
{
printf("Parent ID %d\n", getpid());
sleep(1);
}
}
return 0;
}