“等待”系统调用中的EINTR

时间:2019-01-15 14:11:46

标签: c linux process signals posix

来自man -e 2 wait

  

呼叫等待(&status)等效于:              waitpid(-1,&status,0);

errno值:

  

未设置EINTR WNOHANG,并且设置了无阻塞信号或SIGCHLD   抓住;参见signal(7)。

因此,根据我的理解,如果我们在“等待”中受阻并收到信号(SIGCHLD),则调用应以-1并将errno设置为EINTR的方式返回。 尽管运行以下代码片段将证明等待实际上已经重新启动(Linux 4.15.0-43,glibc 2.23):

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

#include <sys/types.h>
#include <sys/wait.h>

static void sigchld_handler(int signo)
{
    const char *const msg = "\nSIGCHLD";

    (void)signo;
    write(STDERR_FILENO, msg, strlen(msg));
}

int main(void)
{
    pid_t wait_ret = 0, pid = 0;
    int status = 0, ret = -1;

    sigaction(SIGCHLD, &(struct sigaction){
       .sa_handler = sigchld_handler,
    }, NULL);

    if ((pid = fork()) < 0)
    {
       perror("\nfork: ");
       goto Exit;
    }

    if (!pid)
    {
       sleep(3);
       return 0;
    }

    if ((wait_ret = wait(&status)) < 0)
    {
       perror("\nwait: ");
       goto Exit;
    }

    fprintf(stderr, "\nwait done, pid %d", wait_ret);
    ret = 0;

Exit:
    fprintf(stderr, "\n");
    return ret;
}

未设置SA_RESTART-为什么“等待”重新启动? 实际输出:

SIGCHLD
wait done, pid 15242

由我期望的输出:

SIGCHLD
wait: Interrupted system call

从shell手动注意kill -CHLD <waiting process>将得到预期的结果。

还请注意,在FreeBSD 11.2上运行代码也将获得预期的结果-等待在子退出时因错误而中断。

1 个答案:

答案 0 :(得分:3)

这里的解释很微妙。您必须先开始:

  

成功后,[wait]返回终止子进程的进程ID;如果出错,则返回-1。

Linux wait(2) manual page

关于errno值的文档需要这样解释: 如果wait()返回-1 ,则表示错误,并且errno设置为EINTR,则解释为SIGCHLD或不受阻碍的(其他)信号中断了等待。但这并不意味着接收SIGCHLD一定会导致wait()失败。特别是,wait()将正常完成,而不是SIGCHLD是由于某个进程的子进程的终止而引起的错误。

在对此处的文档过于批评之前,请考虑一下

  • SIGCHLD可能由于孩子被终止以外的其他原因而分娩
  • 错误描述适用于该组中的所有功能,并且其中某些功能只能等待特定的子代。这些SIGCHLD可能会被一些他们不等待的孩子打断。

文档的措辞似乎也暗示SIGCHLD可能导致其中一个或多个功能因EINTR而失败,即使该信号被阻止。我会发现这种行为令人惊讶,但请记住我是从哪里开始的:该结果是对EINTR错误的可能解释之一,并不一定意味着它实际上可能发生。