前置知识
SIGCHLD产生条件
- 子进程终止时
- 子进程接收到SIGSTOP信号停止时
- 子进程处于停止态,接受到SIGCONT后唤醒时
sigaction函数
修改信号处理动作(通常在Linux用来注册一个信号的捕捉函数)
int sigaction(int signum, const struct sigaction * act, struct sigaction *oldact);
成功:0 ; 失败:-1
参数:
- act: 传入参数,新的处理方式;
- oldact:传出参数,旧的处理方式。
struct sigaction 结构体
struct sigaction {
void (*sa_handler)(int);
sigset_t sa_mask;
int sa_flags;
}
- sa_handler: 指定信号捕捉后的处理函数名(即注册函数)。也可赋值为SIG_IGN表忽略 或 SIG_DFL 表执行默认动作。
- sa_mask: 调用信号处理函数时,所要屏蔽的信号集合(信号屏蔽字)。注意:仅在处理函数被调用期间屏蔽生效,是临时性设置。
- sa_flags: 通常设置为0, 表使用默认属性。
实现
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<signal.h>
#include<sys/wait.h>
#include<pthread.h>
void sys_err(const char *str){
perror(str);
exit(1);
}
void catch_child(int signo){ //捕捉函数
pid_t wpid;
//会出现僵尸进程: 因为在同一时间点,死的进程太多,处理不过来。因为系统只是知道哪个是死的,不知道死的顺序。
// wpid = wait(NULL);
// if(wpid == -1){
// sys_err("wait error");
// } else {
// printf("catch child id = %d\n", wpid);
// }
while((wpid = wait(NULL)) != -1 ){
printf("--------catch child id = %d\n", wpid);
}
return ;
}
int main(int argc, char *argv[]){
pid_t pid;
int i;
for(i = 0; i<15; i++){ //创建子进程
if((pid = fork()) == 0){
break;
}
}
if(15 == i){
struct sigaction act;
act.sa_handler = catch_child; //设置回调函数
sigemptyset(&act.sa_mask); //设置捕捉函数执行期间屏蔽字
act.sa_flags = 0; //设置默认属性:本信号自动屏蔽
sigaction(SIGCHLD, &act, NULL); //注册信号捕捉函数
printf("Parent, pid = %d\n", getpid());
while(1);
} else {
printf("Child, pid = %d\n", getpid());
}
return 0;
}
步骤
- 创建子进程
- 编写捕捉函数
- 在父进程中注册
演示
- 有僵尸子进程
执行 ps ajx查看进程
- 无僵尸进程