6

我正在执行一项任务以构建一个简单的 shell,并且我正在尝试添加一些尚不需要的功能,但我遇到了管道问题。

一旦我的命令被解析,我就会派生一个进程来执行它们。这个过程是一个子程序,将执行命令,如果只剩下一个,否则就分叉。父级将执行第一个命令,子级将处理其余的。管道已设置并正常工作。

然后我的主进程调用wait(),然后输出提示。当我执行类似的命令时ls -la | cat,会在 cat 的输出之前打印提示。

我尝试wait()为每个应该执行的命令调用一次,但是第一次调用有效并且所有后续调用都返回ECHILD

如何强制我的主线程等到所有孩子,包括孩子的孩子,退出?

4

3 回答 3

7

你不能。要么让你的子进程等待它的子进程并且在它们都被等待之前不要退出,或者从同一个进程中派生所有子进程。

于 2012-10-10T15:13:24.553 回答
4

请参阅此答案如何wait()处理子进程:如何等待 fork() 调用的所有子进程完成?

没有办法等孙子;您需要在每个进程中实现等待逻辑。这样,每个孩子只会在其所有孩子退出后退出(然后将包括所有孙辈)。

于 2012-10-10T15:13:20.137 回答
0

既然你在谈论孙子,你显然是以级联的方式产生孩子。这是实现管道的一种可能方式。

但请记住,管道的返回值(echo $?在终端中执行时得到的值)是从最右边的命令返回的值。

这意味着您需要在此级联实现中从右到左生成子级。您不想丢失该返回值。

现在假设为了简单起见我们只讨论内置命令(没有额外调用 fork() 和 execve()),一个有趣的事实是,在某些 shell 中,如“zsh”,最右边的命令不是甚至分叉。我们可以通过一个简单的管道命令看到这一点,例如:

export stack=OVERFLOW | export overflow=STACK

然后使用命令env,我们可以欣赏环境变量中溢出=堆栈的持久性。它表明最右边的命令没有在子shell中执行,而在子shell中执行了export stack=OVERFLOW

注意:在像“sh”这样的 shell 中,情况并非如此。

现在让我们使用一个基本的管道命令来为这个级联实现提供一个可能的逻辑。

cat /dev/random | head

注意:即使cat /dev/random据说是一个永无止境的命令,它也会在命令head完成读取cat /dev/random. 这是因为标准输入在head完成后关闭,并且命令cat /dev/random中止,因为它在损坏的管道中写入。

逻辑:

  1. 父进程(你的 shell)看到有一个管道要执行。然后它将分叉两个进程。父母留在你的外壳,它会等待孩子返回,并存储返回的值。

  2. 在第一代子的上下文中:(试图执行管道最右边的命令)它看到该命令不是最后一个命令,它会再次 fork() (我称之为“级联实现”)。现在 fork 已经完成,父进程将首先执行它的所有任务 ( head -1),然后它会关闭它的标准输入和标准输出,然后等待它的子进程。这对于首先关闭标准输入和标准输出,然后调用 wait() 非常重要。如果在标准输入上读取,则关闭标准输出会将 EOF 发送到父级。关闭标准输入确保试图在管道中写入的孙子中止,并出现“管道损坏”错误。

  3. 在孙子的上下文中:它看到它是管道的最后一个命令,它只会执行命令并返回它的值(它关闭标准输入和标准输出)。

于 2020-03-21T04:06:09.517 回答