waitpid() return 值 0 和 errno EINTR

waitpid() return value 0 along with errno EINTR

我正在尝试编写一个程序,在该程序中,我从父级派生出一个子级,并使用处理程序处理 SIGCHLD 信号,我在其中使用了 waitpid()。然而,当我执行它时,我有时会从 waitpid 获得一个 return 值 0,同时 errno 被设置为 EINTR。这是什么意思?

这是我的 SIGCHLD 处理程序:

pid_t pid;
int status;

while((pid = waitpid(-1, &status, WNOHANG|WUNTRACED)) > 0)
{
    printf("Handler reaped child %d\n", (int)pid);
    if(WIFEXITED(status))
    {
        deletejob(job_list, pid);
    }
    else if(WIFSIGNALED(status))
    {
        deletejob(job_list, pid);
    }
    else if(WIFSTOPPED(status))
    {
        struct job_t *job = getjobpid(job_list, pid);
        job->state = ST;
    }
}

printf("%d %d\n", pid, errno);
if(errno != ECHILD)
{
    unix_error("waitpid error");
}

return;

这是父函数,我在其中 fork 子函数:

    pid_t pid;
    sigset_t block_set;
    int file_descriptor;
    if(tok.outfile == NULL)
    {
        file_descriptor = STDOUT_FILENO;
    }
    else
    {
        file_descriptor = open(tok.outfile, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
    }

    sigemptyset(&block_set);
    sigaddset(&block_set, SIGCHLD);
    sigaddset(&block_set, SIGINT);
    sigaddset(&block_set, SIGTSTP);
    sigprocmask(SIG_BLOCK, &block_set, NULL);

    pid = fork();

    if(pid == 0)
    {
        sigprocmask(SIG_UNBLOCK, &block_set, NULL);
        setpgid(0, 0);
        dup2(file_descriptor, 1);

        while(execve(tok.argv[0], tok.argv, environ) < 0)
        {
            exit(0);
        }
    }
    else
    {
        if(bg == 1)
        {
            addjob(job_list, pid, BG, cmdline);
            sigprocmask(SIG_UNBLOCK, &block_set, NULL);
            int jid = pid2jid(pid);
            printf("[%d] (%d) %s\n", jid, pid, cmdline);
        }
        else if(bg == 0)
        {
            addjob(job_list, pid, FG, cmdline);
            sigprocmask(SIG_UNBLOCK, &block_set, NULL);
        }

        if(bg == 0)
        {
            struct job_t *job = getjobpid(job_list, pid);
            while(pid == fgpid(job_list))
            {
                sleep(1);
            }
        }
    }
}

return;

当用 WNOHANGwaitpid() returns 0 调用时,没有更多的 children 可以收割。当 child 进程退出时,您的 SIGCHLD 处理程序会被调用,因此您知道总会有至少一个要收割。但是因为多个信号没有排队,所以可能有不止一个 child 进程要收割。

那么这个 while 循环做了什么:

while((pid = waitpid(-1, &status, WNOHANG|WUNTRACED)) > 0)

基本上就是说"call waitpid() to reap the child I know is waiting, and then keep calling it over and over to reap any additional children which may happen to be available, until it returns 0, at which point I know I've reaped all the children that are available."

因此这里返回0并不是一个错误,它是一个蓄意获取未知数量的children的装置。 waitpid() 没有设置 errno,在这种情况下,所以 EINTR 一定是在之前的系统调用被中断时设置的。

一般来说,你不应该检查 errno 除非一个函数 return 是一个错误,虽然有一些不寻常的情况(strtol() 是一个,其中一个 [=44= 0 的 ] 可能意味着解析的数字是 0,或者可能意味着有错误),其中 return 值没有明确指示错误。在这些情况下,您可以在调用函数之前将 errno 设置为 0,如果 return 值表明 可能 是一个错误,您可以检查 errno 以查看是否已设置。