将等待和waitpid阻止SIGCHLD并在Linux返回时取消阻止它吗?

时间:2017-05-13 04:18:47

标签: c linux process signals system-calls

以下是我的代码:

void handler(int n) {
    printf("handler %d\n", n);
    int status;
    if (wait(&status) < 0)
        printf("%s\n", strerror(errno));
}

int main() {
    struct sigaction sig;
    sigemptyset(&sig.sa_mask);
    sig.sa_handler = handler;
    sig.sa_flags = 0;
    sig.sa_restorer = NULL;
    struct sigaction sigold;
    sigaction(SIGCHLD, &sig, &sigold);
    pid_t pid;
    int status;
    printf("before fork\n");
    if ((pid = fork()) == 0) {
        _exit(127);
    } else if (pid > 0) {
        printf("before waitpid\n");
        if (waitpid(pid, &status, 0) < 0)
            printf("%s\n", strerror(errno));
        printf("after waitpid\n");
    }
    printf("after fork\n");
    return 0;
}

输出结果为:

  叉前

     在waitpid之前

     

处理程序17

     

没有子进程

     在waitpid之后

     叉后

所以,我认为waitpid将阻止SIGCHLD并等待孩子终止,一旦孩子终止,它会做一些事情并在SIGCHLD返回之前解锁,这就是为什么我们看到&#34 ;没有子进程&#34; 错误和&#34;在waitpid&#34; 之后是&#34;处理程序17&#34; ,我是对?如果不是,真相是什么?如何解释输出序列?是否有针对Linux或其他类似的规范进行检查?

1 个答案:

答案 0 :(得分:3)

流程的退出信息只能收集一次。您的输出显示在代码位于waitpid()时调用的信号处理程序,但处理程序调用{​​{1}}并收集子项的信息(您在不报告的情况下丢弃)。然后,当您回到wait()时,已经收集了子退出状态,因此waitpid()没有任何内容可以报告,因此“没有子进程”#39;错误。

这是您的计划的改编。它通过在信号处理函数中使用waitpid()来滥用它,但它似乎工作,尽管如此,在运行macOS Sierra 10.12.4的Mac上进行测试(使用GCC 7.1.0进行编译)。

printf()

示例输出:

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

static void handler(int n)
{
    printf("handler %d\n", n);
    int status;
    int corpse;
    if ((corpse = wait(&status)) < 0)
        printf("%s: %s\n", __func__, strerror(errno));
    else
        printf("%s: child %d exited with status 0x%.4X\n", __func__, corpse, status);
}

int main(void)
{
    struct sigaction sig = { 0 };
    sigemptyset(&sig.sa_mask);
    sig.sa_handler = handler;
    sig.sa_flags = 0;
    sigaction(SIGCHLD, &sig, NULL);
    pid_t pid;
    printf("before fork\n");
    if ((pid = fork()) == 0)
    {
        _exit(127);
    }
    else if (pid > 0)
    {
        printf("before waitpid\n");
        int status;
        int corpse;
        while ((corpse = waitpid(pid, &status, 0)) > 0 || errno == EINTR)
        {
            if (corpse < 0)
                printf("loop: %s\n", strerror(errno));
            else
                printf("%s: child %d exited with status 0x%.4X\n", __func__, corpse, status);
        }
        if (corpse < 0)
            printf("%s: %s\n", __func__, strerror(errno));
        printf("after waitpid loop\n");
    }
    printf("after fork\n");
    return 0;
}

状态值0x7F00是before fork before waitpid handler 20 handler: child 29481 exited with status 0x7F00 loop: Interrupted system call main: No child processes after waitpid loop after fork 的正常编码。来自Linux的macOS的信号编号不同;这完全是允许的。

要获得在Linux上编译的代码(用于测试的Centos 7和Ubuntu 16.04 LTS),使用GCC 4.8.5(几乎是antediluvian - 当前版本是GCC 7.1.0)和5.4.0,使用命令行:

_exit(127)

我在第一个标头之前添加了$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \ > -Wstrict-prototypes -Wold-style-definition sg59.c -o sg59 $ ,并使用了:

#define _XOPEN_SOURCE 800

使用GCC 4.8.5初始化结构。那种shenanigan偶尔是避免编译器警告的痛苦必要。我注意到尽管公开POSIX符号需要struct sigaction sig; memset(&sig, '\0', sizeof(sig)); ,但GCC 5.4.0接受初始值设定项(#define)没有问题。

当我运行该程序时,我得到的输出与congcomment报告的内容非常相似:

struct sigaction sig = { 0 };

确实很奇怪,在Linux上,进程发送了SIGCHLD信号,但before fork before waitpid handler 17 handler: No child processes main: child 101681 exited with status 0x7F00 main: No child processes after waitpid loop after fork 无法在信号处理程序中等待它。这至少是违反直觉的。

我们可以讨论wait()的第一个论点是waitpid()而不是pid的重要性。因为第一次从子节点收集信息,所以在循环的第二次迭代中错误是不可避免的。在实践中,这并不重要。一般情况下,最好使用0或其左右 - 根据具体情况,waitpid(0, &status, WNOHANG)代替0可能会更好。