在以下情况下如何避免read()挂起?

时间:2018-11-30 17:19:16

标签: c linux pipe fork zombie-process

我有一些代码可以派生第三方应用程序,并将其标准输出重定向到父进程,大致如下(此处为简洁起见,没有错误处理):

char* args[] = {"/path/to/3rd/party/binary", "with", "args", NULL};

int fds[2];
pipe2(fds, O_CLOEXEC);

pid_t pid = fork();
if (pid == 0)
{
    dup2(fds[1], STDOUT_FILENO);
    execvp(args[0], args);
    _exit(1);
}
else
{
    close(fds[1]);

    char buf[1024];
    int bytes_read;
    while ((bytes_read = read(fds[0], buf, sizeof buf - 1)) > 0)
    {
        buf[bytes_read] = '\0';
        printf("%s", buf);
    }

    close(fds[0]);
    waitpid(pid, NULL, 0);
}

我没有第三方应用程序的代码,它是专有的二进制文件。当在终端中使用与上面的代码相同的参数运行第三方应用程序时,最终将完成。但是,使用上面的代码分叉第三方二进制文件时,它并没有完成,而是变成了僵尸进程,并且上面的代码挂在了read()调用上。

自身分叉的第三方二进制文件分叉了两个守护进程(同样,我无法控制的专有二进制文件),我认为这是造成此问题的原因。分叉的守护程序进程将具有重复的文件描述符的副本,从而阻止read()完成。实际上,如果将dup2()调用替换为:

dup3(fds[1], STDOUT_FILENO, O_CLOEXEC);

子进程完成,但是没有输出重定向到父进程。另外,如果将上面的代码修改为不通过管道将任何输出重定向到父级,则子级进程将正确完成。

在这种情况下,是否可以通过某种方式防止在read()调用上挂起,还是我需要诉诸某种形式的非阻塞I / O?

更新;使用简单的popen()会遇到相同的问题。

(后续跟踪:read() hangs on zombie process

1 个答案:

答案 0 :(得分:2)

您需要专门忽略SIGCHLD。获得僵尸是您的责任,但在<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>中被阻止时,您将无法做到。如果您吞下read后致电read,您将永远停留在SIGCHLD