如何在主程序中使用`SIGCHLD`处理程序调用`wait()`设置在插件中安全`waitpid()`

问题描述:

我正在为需要执行某些子进程并读取其输出的工具箱编写模块。但是,使用该工具包的主程序也可能会产生一些子进程,并为SIGCHLD建立一个信号处理程序,该程序调用wait(NULL)来摆脱僵尸进程。因此,如果在我的waitpid()子程序中创建了子进程,则在调用信号处理程序之前处理子进程,因此信号处理程序中的wait()将等待下一个进程结束(可能需要永远)。此行为在the man page of waitpid(请参见受赠方2)中进行了描述,因为linux实现似乎不允许wait()系列处理SIGCHLD。我试过popen()posix_spawn(),他们都有同样的问题。我也尝试使用双重fork(),以便直接的孩子立即存在,但我仍然不能保证在接收到SIGCHLD后调用waitpid()。我的问题是,如果程序的其他部分设置了一个信号处理程序,它调用wait()(也许它应该调用waidpid,但这不是我可以控制的),是否有一种方法可以安全地执行子进程而不会覆盖SIGCHLD处理程序(因为它可能在某些程序中有用)或任何僵尸进程。如何在主程序中使用`SIGCHLD`处理程序调用`wait()`设置在插件中安全`waitpid()`

一个小程序,这说明问题是在这里(注意,主程序从长远来看孩子退出后唯一的出口,而不是短单,这是它直接与waitpid函数等待()):

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

static void 
signalHandler(int sig) 
{ 
    printf("%s: %d\n", __func__, sig); 
    int status; 
    int ret = waitpid(-1, &status, 0); 
    printf("%s, ret: %d, status: %d\n", __func__, ret, status); 
} 

int 
main() 
{ 
    struct sigaction sig_act; 
    memset(&sig_act, 0, sizeof(sig_act)); 
    sig_act.sa_handler = signalHandler; 
    sigaction(SIGCHLD, &sig_act, NULL); 

    if (!fork()) { 
     sleep(20); 
     printf("%s: long run child %d exit.\n", __func__, getpid()); 
     _exit(0); 
    } 

    pid_t pid = fork(); 
    if (!pid) { 
     sleep(4); 
     printf("%s: %d exit.\n", __func__, getpid()); 
     _exit(0); 
    } 
    printf("%s: %d -> %d\n", __func__, getpid(), pid); 

    sleep(1); 
    printf("%s, start waiting for %d\n", __func__, pid); 
    int status; 
    int ret = waitpid(pid, &status, 0); 
    printf("%s, ret: %d, pid: %d, status: %d\n", __func__, ret, pid, status); 

    return 0; 
} 

如果该过程是单线程的,可以暂时(使用sigprocmask)fork/waitpid来阻塞CHLD信号,然后再次解除阻塞。

不要忘记在分叉的孩子中解锁信号 - 尽管POSIX声明当进程启动时信号掩码是未定义的,但大多数现有程序希望它完全未被设置。

+0

这不起作用的原因很多,除了没有办法知道主程序是否是单线程的:1)我说过我不想重写信号处理程序,因为当我设置了一个不同的信号处理程序(可能是'SIG_IGN')时,主应用程序想要等待的进程可能会在任何时候完成。 2)如果忽略'SIG_CHLD','wait'将返回一个错误,并且不能获得子进程的退出状态,所以我最好设置自己的处理程序而不是忽略它。 – yuyichao

+0

首先,如果程序是单线程的,那么知道它是否是单线程是微不足道的 - 根据定义,它有很多方法可以确保从库中。其次,您将信号处理程序与信号掩码混淆(不同的事情,请阅读sigprocmask的手册页)。第三,即使忽略了CHLD,waitpid也不会返回错误,直到孩子获得成功,所以它可以工作(试用) - 你可能无法可靠地获得退出状态,但可以安全地等待孩子的等待。 –

+0

如果您想在这里找到答案,请正确阅读答案,并在提出答案错误之前尝试解决。另外,如果你问错了问题,如果你的问题得到了错误的答案,不要感到惊讶。 –