代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
void handler(int signo)
{
int status;
pid_t pid;
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()
{
pid_t pid;
int i = 0;
//循环创建子进程
for(i = 0; i < 10; i++)
{
pid = fork();
if (pid == 0)
{
break;
}
else if (pid < 0)
{
perror("pid error:");
exit(1);
}
}
if (pid > 0)
{
sigset_t set,oldmask;
sigemptyset(&set);
sigemptyset(&oldmask);
sigaddset(&set,SIGCHLD);
//屏蔽
sigprocmask(SIG_BLOCK,&set,NULL);
struct sigaction act;
//安装信号
act.sa_flags = 0;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
//父进程
sigaction(SIGCHLD,&act,NULL);
//开启
sigprocmask(SIG_SETMASK,&oldmask,NULL);
while (1);
}
else if (pid == 0)
{
int n = 1;
while (n--) {
printf("child ID %d\n", getpid());
sleep(1);
}
return i + 1;
}
return 0;
}
问题
如果我们把while循环改成if,相当于每个子进程运行完后就会发送SIGCHLD信号,父进程通过捕获,用waitpid来回收子进程,可以吗?
void handler(int signo)
{
int status;
pid_t pid;
if ((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));
}
}
但是,运行的效果却并不如我们所想象的。这是为什么呢?
当父进程收到第一个信号的时候,将会执行handler这个函数,在执行函数期间,子进程仍会发出信号,在Linux中1-31号信号是非实时信号,不支持排队,信号丢失了,造成了这个原因。
注意点
代码如下(示例):
sigset_t set,oldmask;
sigemptyset(&set);
sigemptyset(&oldmask);
sigaddset(&set,SIGCHLD);
//屏蔽
sigprocmask(SIG_BLOCK,&set,NULL);
struct sigaction act;
//安装信号
act.sa_flags = 0;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
//父进程
sigaction(SIGCHLD,&act,NULL);
//开启
sigprocmask(SIG_SETMASK,&oldmask,NULL);
为什么要采用屏蔽和开启信号的方式呢?
原因在于在程序运行中,为了避免父进程还没注册完信号,子进程就发送信号的情况。信号阻塞时发送,信号的递送会等到信号解除后。