SIGCHLD信号产生的条件

Untitled

使用SIGCHLD信号解决僵尸进程问题

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/wait.h>

void myFun(int num) {
    printf("捕捉到的信号 :%d\\n", num);
    // 回收子进程PCB的资源
    // 使用wait()的问题:
    //  - 如果直接使用wait():由于常规信号不可排队的特性,使得部分SIGCHLD信号被丢弃
    //  - 如果循环调用wait():则父进程会一直阻塞在回调函数中
    // while(1) {
    //     wait(NULL); 
    // }
    while(1) {
	     // waitpid 设置非阻塞
       int ret = waitpid(-1, NULL, WNOHANG);
       if(ret > 0) {
           printf("child die , pid = %d\\n", ret);
       } else if(ret == 0) {
           // 说明还有子进程活着,返回父进程
           break;
       } else if(ret == -1) {
           // 没有子进程了。返回父进程
           break;
       }
    }
}

int main() {

    // 提前设置好阻塞信号集,阻塞SIGCHLD,因为有可能子进程很快结束,父进程还没有注册完信号捕捉
    // 这种情况会发生段错误
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGCHLD);
    sigprocmask(SIG_BLOCK, &set, NULL);

    // 创建一些子进程
    pid_t pid;
    for(int i = 0; i < 20; i++) {
        pid = fork();
        if(pid == 0) {
            break;
        }
    }

    if(pid > 0) {
        // 父进程

        // 捕捉子进程死亡时发送的SIGCHLD信号
        struct sigaction act;
        act.sa_flags = 0;
        act.sa_handler = myFun;
        sigemptyset(&act.sa_mask);
        sigaction(SIGCHLD, &act, NULL);

        // 注册完信号捕捉以后,解除阻塞
        sigprocmask(SIG_UNBLOCK, &set, NULL);

        while(1) {
            printf("parent process pid : %d\\n", getpid());
            sleep(2);
        }
    } else if( pid == 0) {
        // 子进程
        printf("child process pid : %d\\n", getpid());
    }

    return 0;
}