我正在尝试为shell执行管道实现。我已经按照以下方式实现了它的工作原理。例如:如果我想这样做,ls | grep x | grep y | grep z,我正在从父进程创建4个子进程并使用它们。有没有其他方法可以做到这一点?
例如:我可以使用以下流程创建它吗?而不是让单个父母过程有4个孩子,而不是'grep z'成为' grep y'并且' grep y'成为' grep x'的孩子等等 ?
我很好奇如何在bash shell中实现管道功能。我尝试下载源代码并理解它但丢失了。
答案 0 :(得分:5)
这取决于您的shell和您调用的程序。
理论上,你可以使用任何具有N个非根节点的可变参数树来组合爆炸的可能性,其中一些是:
shell shell shell
/ / \ / \
grep x ls grep y ls grep y
/ \ \ \ / \
ls grep y grep x grep z grep x grep z
\
grep z
在继续下一个命令之前,POSIX标准shell是required to wait for the last stage to finish。由于进程在子进程上只能wait,这意味着最后一个阶段必须是主shell的子进程。 POSIX津贴是另外等待所有阶段,这是bash
和大多数其他shell做的事(尝试sleep 5 | true
)。
这意味着bash
将所有进程作为自己的子进程启动,并且您可以使用例如strace -f -e clone bash -c 'sleep 5 | sleep 5 | sleep 5'
或sleep 5 | sleep 5 | sleep 5 & pstree -p $$
,如果您不想在bash源代码中execute_pipeline
中学习execute_cmd.c
。
这样做的另一个好处是允许bash的PIPESTATUS数组和pipefail选项对管道中所有阶段的状态起作用,如果所有阶段都不是直接的话,你就无法获得儿童过程。
另一个考虑因素是程序很少处理他们没有预料到的孩子。充其量,你会得到一个僵尸进程,最坏的情况是它会干扰进程的正确性。这意味着ls
永远不应该是grep
的直接孩子,反之亦然。但是,您可以通过double fork将其设为孙子,以便init
可以对此负责。
所以是的,您可以使用您想要的任何配置,但在实践中(例如bash
,dash
,ash
和zsh
),它可以使用往往是扁平的。