wait 和 waitpid 会阻塞 SIGCHLD 并在 Linux 中返回时解除阻塞吗? [英] Will wait and waitpid block SIGCHLD and unblock it when they return in Linux?

查看:116
本文介绍了wait 和 waitpid 会阻塞 SIGCHLD 并在 Linux 中返回时解除阻塞吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的代码来检查这个:

Here is my code to examine this:

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的阻塞,这就是为什么我们看到无子进程" 错误和 "after waitpid" 是在 "handler 17" 之后,对吗?如果不是,真相是什么?如何解释输出序列?是否有 Linux 或类似的规范需要检查?

So, I think waitpid will block SIGCHLD and wait for child to terminate, once the child terminates, it will do something and the unblock the SIGCHLD before it returns, that's why we see "No child processes" error and "after waitpid" is after "handler 17", am I right? if not, what is the truth? How to explain the output sequence? Is there a specification for Linux or something like that to check?

推荐答案

一个进程的退出信息只能收集一次.您的输出显示在您的代码处于 waitpid() 时正在调用的信号处理程序,但处理程序调用 wait() 并收集子进程的信息(您抛出的信息)离开而不报告).然后当你回到 waitpid() 时,已经收集了子退出状态,所以 waitpid() 没有任何东西可以报告,因此`没有子进程' 错误.

The exit information for a process can only be collected once. Your output shows the signal handler being called while your code is in waitpid(), but the handler calls wait() and that collects the information of the child (which you throw away without reporting). Then when you get back to waitpid(), the child exit status has been collected, so there's nothing left for waitpid() to report on, hence the `no child processes' error.

这是对您的程序的改编.它通过在信号处理函数中使用 printf() 来滥用某些东西,但尽管如此,它似乎仍然有效,在运行 macOS Sierra 10.12.4(使用 GCC 7.1.0 编译)的 Mac 上进行测试.

Here's an adaptation of your program. It abuses things by using printf() inside the signal handler function, but it seems to work despite that, testing on a Mac running macOS Sierra 10.12.4 (compiling with GCC 7.1.0).

#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;
}

示例输出:

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

状态值 0x7F00 是 _exit(127) 的正常编码.macOS 和 Linux 的信号编号不同;这是完全允许的.

The status value 0x7F00 is the normal encoding for _exit(127). The signal number is different for macOS from Linux; that's perfectly permissible.

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

To get the code to compile on Linux (Centos 7 and Ubuntu 16.04 LTS used for the test), using GCC 4.8.5 (almost antediluvian — the current version is GCC 7.1.0) and 5.4.0 respectively, using the command line:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
>     -Wstrict-prototypes -Wold-style-definition sg59.c -o sg59
$

我在第一个标题之前添加了 #define _XOPEN_SOURCE 800,并使用了:

I added #define _XOPEN_SOURCE 800 before the first header, and used:

struct sigaction sig;
memset(&sig, '\0', sizeof(sig));

使用 GCC 4.8.5 初始化结构.这种恶作剧有时是避免编译器警告的痛苦必要.我注意到虽然 #define 是暴露 POSIX 符号所必需的,但 GCC 5.4.0 毫无问题地接受了初始值设定项 (struct sigaction sig = { 0 };).

to initialize the structure with GCC 4.8.5. That sort of shenanigan is occasionally a painful necessity to avoid compiler warnings. I note that although the #define was necessary to expose POSIX symbols, the initializer (struct sigaction sig = { 0 };) was accepted by GCC 5.4.0 without problems.

然后当我运行程序时,我得到的输出与 cong 报告在 评论:

When I then run the program, I get very similar output to what cong reports getting in a comment:

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

奇怪的是,在 Linux 上,进程被发送了一个 SIGCHLD 信号,但 wait() 不能在信号处理程序中等待它.这至少是违反直觉的.

It is curious indeed that on Linux, the process is sent a SIGCHLD signal and yet wait() cannot wait for it in the signal handler. That is at least counter-intuitive.

我们可以讨论 waitpid() 的第一个参数是 pid 而不是 0 的重要性;错误在循环的第二次迭代中是不可避免的,因为第一次从孩子那里收集信息.在实践中,这里无关紧要.一般来说,最好使用 waitpid(0, &status, WNOHANG) 或类似的东西——取决于上下文,0 而不是 WNOHANG可能会更好.

We can debate how much it matters that the first argument to waitpid() is pid rather than 0; the error is inevitable on the second iteration of the loop since the first collected the information from the child. In practice, it doesn't matter here. In general, it would be better to be using waitpid(0, &status, WNOHANG) or thereabouts — depending on context, 0 instead of WNOHANG might be better.

这篇关于wait 和 waitpid 会阻塞 SIGCHLD 并在 Linux 中返回时解除阻塞吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆