前提介绍
信号捕捉特性:
1. 进程正常运行时,默认pcb中有一个信号屏蔽字,假定为*,它决定了进程自动屏蔽哪些信号,当注册了某个信号捕捉函数,捕捉到该信号以后,要调用该函数,而该函数有可能执行很长时间,在这期间所屏蔽的信号不由来指定,而是用sa_mask来指定,调完信号处理函数,再次恢复
2. xxx信号捕捉函数执行期间,xxx信号自动被屏蔽
3. 阻塞的常规信号不支持排队,产生多次只记录一次。(即前31个)
SIGCHLD信号
子进程在暂停或者退出的时候回发送SIGCHLD信号,可以通过捕捉SIGCHLD
信号来回收子进程。
#include<unistd.h>
#include<sys/wait.h>
#include<signal.h>
void catch_sig(int num){
pid_t wpid;
while( (wpid = waitpid(-1,NULL,WNOHANG)) > 0){
printf("wait child %d ok\n",wpid);
}
}
int main(){
int i =0;
pid_t pid;
//在创建子进程之前屏蔽SIGCHLD信号
sigset_t myset,oldset;
sigemptyset(&myset);
sigaddset(&myset,SIGCHLD);
sigprocmask(SIG_BLOCK,&myset,&oldset);
for(i = 0; i< 10; i++){
pid = fork();
if(pid == 0){
break;
}
}
if(i == 10){
struct sigaction act;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
act.sa_handler = catch_sig;
sigaction(SIGCHLD,&act,NULL);
//解除屏蔽信号
sigprocmask(SIG_SETMASK,&oldset,NULL);
while(1){
sleep(1);
}
}
else if(i < 10){
printf("i am %d child, pid = %d\n",i,getpid());
//sleep(i);
}
}
-
信号执行函数,回收子进程,可以用循环,一次会回收多个,不需要每个进程结束都触发信号,从信号处理函数中回收,这样每次只能回收一个。
-
SIGCHLD信号的注册一定要在进程创建之前(或者在进程结束之前完成)完成。(否则会出现进程已经结束,信号没有捕捉到,即僵尸进程)
处理:
可以在创建子进程之前屏蔽SIGCHLD信号
sigset_t myset,oldset;
sigemptyset(&myset);/清空
sigaddset(&myset,SIGCHLD);
sigprocmask(SIG_BLOCK,&myset,&oldset);//oldset 保留现场,设置了SIGHLD到阻塞信号集。
注册信号之后,需要解除屏蔽现场:
sigpromask(SIG_SETMASK,&oldset,NULL);