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

时间:2014-01-21 14:30:37

标签: linux fork waitpid sigchld

我正在为一个工具包编写一个模块,它需要执行一些子进程并读取它们的输出。但是,使用该工具包的主程序也可能产生一些子进程并为SIGCHLD设置一个信号处理程序,它调用wait(NULL)来摆脱僵尸进程。因此,如果子进程在我的waitpid()内创建退出,则在调用信号处理程序之前处理子进程,因此信号处理程序中的wait()将等待下一个进程结束(这可能需要永远)。这种行为在the man page of waitpid中描述(请参阅受助者2),因为linux实现似乎不允许wait()系列处理SIGCHLD。我尝试了popen()posix_spawn(),但他们都遇到了同样的问题。我还尝试使用双fork(),以便直接存在子项,但我仍然无法保证在waitpid()收到后调用SIGCHLD

我的问题是,如果程序的其他部分设置了一个调用wait()的信号处理程序(也许它应该调用waidpid,但这不是我能控制的),有没有办法安全地执行子进程而不覆盖SIGCHLD处理程序(因为它可能在某些程序中有用)或任何僵尸进程。

一个显示问题的小程序在这里(注意主程序只在长时间子进程退出后退出,而不是短进程,它直接等待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;
}

1 个答案:

答案 0 :(得分:0)

如果进程是单线程的,您可以暂时阻止CHLD信号(使用sigprocmask),fork / waitpid,然后再次取消阻塞。

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