0

手册页system() 指出“在执行命令期间,SIGCHLD 将被阻止”
在我的代码中,我正在安装一个处理程序,用于SIGCHLD将某些内容写入日志并忽略信号。我这样做是为了了解在我的日志中退出的孩子并避免僵尸进程。

使用system()通话时出现问题。有了这个处理程序,system()总是返回-1而不是孩子的退出代码。但是根据手册页的上述引用,system()应该处理来自孩子的信号。

我究竟做错了什么?

我的代码如下:

static void handleSignal(int signum, siginfo_t* inf, void* ctx) {
    cout << "in signal " << signum << endl;
}

int main() {
    struct sigaction chldsa, prevchld;
    chldsa.sa_sigaction = &handleSignal;
    sigemptyset(&chldsa.sa_mask);
    chldsa.sa_flags = SA_SIGINFO | SA_RESTART | SA_NOCLDSTOP | SA_NOCLDWAIT;
    if (sigaction(SIGCHLD, &chldsa, &prevchld) == -1) {
        cout << "Failed setting sigaction SIGCHLD " << errno;
    }

    int x = system("exit 1");
    cout << "RET=" << x << endl;
}
4

2 回答 2

3

如果您只想摆脱 theSA_NOCLDWAIT及其影响,只需在处理程序末尾获取所有子项即可SIGCHLD

编辑添加:刚刚验证了以下内容:

    pid_t p;

    /* Reap all pending child processes */
    do {
        p = waitpid(-1, NULL, WNOHANG);
    } while (p != (pid_t)0 && p != (pid_t)-1);

最后在SIGCHLD处理程序中运行时工作正常(已安装SA_NOCLDSTOP |SA_RESTART | SA_SIGINFO)。没有僵尸,并system()返回正确的退出状态。

请注意,根据 POSIX.1-2004,这是 Linuxwaitpid()中的异步信号安全WNOHANG函数,这意味着调用永远不会阻塞,因此上述循环是安全的。

但是,由于标准信号没有排队,因此如果两个或多个进程以正确的时间退出,其中一个在您的父进程中执行子回收循环之后但在信号处理程序返回之前退出,则可能是一个或更多的孩子没有立即收获。但是,当下一个子进程退出时,它们会。为了缓解这种情况,您应该定期运行上述循环,或者作为程序正常操作的一部分,或者通过定时器中断。循环永远不会阻塞,并且消耗很少的 CPU 时间。

请记住,waitpid(-1, NULL, WNOHANG)如果有活着的孩子,如果没有活着的孩子,或者刚刚收获的进程的进程ID,将返回-1errno == ECHILD。(如果您愿意,您可以在日志记录中使用循环,使用非 NULL 状态指针来捕获已退出的每个子节点的退出状态,并将其记录下来。您甚至可以将定期计时器挂钩到同一个信号处理程序, 例如。)

于 2013-07-09T10:06:43.873 回答
1

Errno 应该显示 SA_NOCLDWAIT 是问题所在。通过提供此标志,您表示您对孩子的状态不感兴趣。这包括由 system() 创建和管理的孩子。系统内部调用 waitpid 将遵循:

POSIX.1-2001 指定如果 SIGCHLD 的处置设置为 SIG_IGN 或为 SIGCHLD 设置了 SA_NOCLDWAIT 标志(请参阅 sigaction(2)),则终止的子进程不会变成僵尸并且调用 wait() 或 waitpid( ) 将阻塞,直到所有子节点都终止,然后失败,errno 设置为 ECHILD。

很明显,你对孩子的地位很感兴趣。您正在检查从 system() 返回的状态。因此,要么移除标志,要么管理所有孩子而无需等待。

如果您处于必须使用 system() 并且具有期望孩子被自动收割的代码的情况下,您可以使用您的处理程序。这不会收集 system() 孩子,因为在通话期间信号被阻止传递。就像是:

while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {}
于 2013-07-08T20:22:21.353 回答