使用 SIGCHLD 处理僵尸

Handling zombies with SIGCHLD

在我的程序中,我正在监听传入的 SIGCHLD 信号以避免僵尸。

代码:

void myhandler(int signo)
{   
    printf("test");
    int status;
    pid_t pid;

    while((pid = waitpid(-1, &status, WNOHANG)) > 0)
        ++count;
}

int main(int argc, char const *argv[])
{

    struct sigaction sigchld_action;
    memset(&sigchld_action,0,sizeof(sigchld_action));
    sigchld_action.sa_handler = &myhandler;
    sigaction(SIGCHLD,&sigchld_action,NULL);

    if(fork() == 0){
        exit(0);
    }
    if(fork()==0){
        exit(0);
    }
    if(fork()==0){
        exit(0);
    }


    while(wait(NULL) > 0)
    ++count;

    return 0;
}

问题是,forked childs 的数量和 printf("test") 的输出数量有时不匹配。分叉的数量 child 大于数字 printf("test").

这个代码段是不是保证没有僵尸?如果是,它如何实现这一目标?它没有打印正确数量的 "test"。 waitpid() 是否在 while 内多次清除死机 childs?

会发生什么,当这个信号处理程序调用时,同时另一个 child 可能会死掉。默认情况下信号将被阻止。(当处理程序为 运行 时,另一个 child 可能会死亡)。当信号处理程序为运行时,waitpid是否清除其信号发送的进程?

此外,计数器不相等。 (static volatile int) 或者我尝试了原子整数。

两件不同的事情正在发生:

  1. printf 不是异步信号安全的,所以你不应该从信号处理程序中调用它。将其替换为 write.
  2. 您只在信号处理程序中递增 count 一次。如果您想知道有多少进程死亡,则需要在 while 循环中增加它。

试试这个代码:

void handler(int signo)
{   
    int status;
    pid_t pid;
    while((pid = waitpid(-1, &status, WNOHANG)) > 0) {
        write(1, "test", 4); /* technically this may result in a partial write and you should loop it, but in practice I think this'll be fine for this example */
        ++count;
    }
}

使用该代码,您可能没有僵尸,并且 test 的数量正确,count 中的值正确。然而,还有一个竞争条件:如果在最终调用 waitpid 和信号处理程序结束之间,另一个子进程死亡,那么将不会收到 SIGCHLD,因此它将成为一个僵尸进程,直到它死后的那个也是。这种边缘情况的解决方案要复杂得多,并且取决于应用程序其余部分的结构。