进程终止后,stdin/stderr是否保证被关闭?

6
我使用pipe()、fork()、dup()和execve()来生成一个子进程。
从父进程的角度,等待子进程的stdin和stdout关闭(即在两者上执行read()返回0),然后再执行waitpid()以收集终止状态是否安全?
还是说可能会出现进程失败但从未关闭stdin和stdout的情况?(因此我的程序永远无法达到waitpid)。
为了澄清,我想知道以下操作是否安全:
while (child_stdin != -1 && child_stdout != -1) {
    poll(...)

    if (got_stdout) {
        n = read(child_stdout);
        if (n >= 0) {
            // do something with output
        } else if (n == 0) {
            child_stdout = -1
        }
    }

    // same for stdin
}

waitpid(child_pid)

或者说孩子进程可能会终止,但我读取()或写入()时不会得到0。

没有信号参与,只是使用了poll()。

2个回答

6
如果进程a生成进程b,并且进程b生成进程c(因此ca的孙子),则b可以终止并关闭其作为stdout的文件描述符的副本(大概率情况下,b的stdout是管道的写端,由监视)。但是c仍然打开该管道,因此a将从b得到SIGCHLD,但不会在管道上得到EOF。换句话说,确实可能发生子进程终止但父进程没有看到管道关闭的情况。
此外,常见编码错误是a仍然保持管道的写端打开状态。在这种情况下,b可以终止并关闭其写端的副本,但父进程永远不会看到管道关闭,因为父进程一直将其保持打开状态。

当父进程从后台运行的子进程接收到SIGCHLD信号时,其标准输入(stdin)会收到EOF。我该如何在SIGCHLD信号处理程序中忽略这个EOF呢? - ToniAz
1
EOF不是一个信号,因此您不能忽略它。除了方便的语言抽象之外,EOF根本不存在。当进程尝试从没有任何数据且所有写端已关闭的管道中读取时,readfread将返回0。fgetcgetc等返回一个整数值,C预处理器通过标签“EOF”进行识别,人们用“管道在所有写入端都关闭且数据耗尽时尝试读取时会出现EOF”的表达式缩短“管道到达EOF”。您永远不会“接收EOF”。 - William Pursell

2
系统会在进程退出时关闭所有文件描述符。可能会发生的情况是,有些输出存储在缓冲区中,尚未刷新。
如果子进程从未终止,则可能会发生您的父进程永远等待并且永远不会到达waitpid()。
编辑:当您的子进程终止时,所有描述符都会关闭,然后您最终将在read()上获得0(或-1如果出现错误!)。因此,您可能需要更改为:
...
if (n > 0) {
    // do something with output
} else if (n <= 0) {
    child_stdout = -1
}

由于以下原因,您的代码无法终止:

if (n >= 0) {  // n > 0 || n == 0
    // ...
} else if (n == 0) {
    // Never reached!
}

啊,是的,当然。那只是一个快速的例子。感谢您的回答。我会选择William的,因为它更加详细。 - Jaka Jančar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接