【问题标题】:How to safely `waitpid()` in a plugin with `SIGCHLD` handler calling `wait()` setup in the main program如何在主程序中调用 `wait()` 设置的 `SIGCHLD` 处理程序的插件中安全地`waitpid()`
【发布时间】:2014-02-11 05:09:05
【问题描述】:

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

我的问题是,如果程序的其他部分设置了一个调用wait() 的信号处理程序(也许它应该调用waidpid,但这不是我可以控制的),有没有办法安全地执行 child进程而不覆盖 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;
}

【问题讨论】:

    标签: linux fork waitpid sigchld


    【解决方案1】:

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

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

    【讨论】:

    • 这不起作用的原因有很多,除了无法知道主程序是否是单线程的:1)我说过我不想覆盖信号处理程序,因为当我设置不同的信号处理程序(可能是SIG_IGN)时,主应用程序想要等待的进程可能随时完成; 2) 如果SIG_CHLD 被忽略,wait 将返回错误并且无法获取子进程的退出状态,所以我最好设置自己的处理程序而不是忽略它。
    • 首先,如果程序是单线程的,那么知道它是否是单线程的就很简单了——根据定义,它是单线程的,并且有很多方法可以从库中确保这一点。其次,您将信号处理程序与信号掩码混淆(不同的东西,请阅读 sigprocmask 的联机帮助页)。第三,即使 CHLD 被忽略,waitpid 也不会返回错误,直到孩子被收割,所以它会工作(试一试) - 你可能无法可靠地获得退出状态,但你可以安全地为孩子 waitpid。跨度>
    • 如果您想在这里得到答案,请正确阅读答案并在声称答案错误之前尝试一下。此外,如果您问错了问题,如果您的问题得到了错误的答案,请不要感到惊讶。
    • 1) 那么如何判断程序是否是单线程的呢?我还没有找到这样的东西。 2)我确实对掩盖与忽略感到困惑(我现在记得我以前也做过一次)。 3) 我试过了,waitpid 确实返回了一个错误。我知道它会等待正确的子进程存在,但正如我的评论所说,问题是“无法获得子进程的退出状态”(OTOH 掩码确实没有这个问题)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-09
    • 2020-02-18
    • 1970-01-01
    • 2018-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多