我有一个简单的脚本 ( pipecommand.sh
),它打印一个前缀,后跟其 PID 和 PPID:
#!/usr/bin/env ksh
echo "$1: PID $$ PPID $PPID" >&2
这个脚本打印它的 PID 和 PPID 并创建一个管道:
#!/usr/bin/env ksh
echo "S: PID $$ PPID $PPID"
./pipecommand.sh A | ./pipecommand.sh B
当我运行脚本时,我得到以下输出(我已经简化了 PID):
S: PID 11 PPID 22
B: PID 33 PPID 11
A: PID 44 PPID 11
好的,A 和 B 都是 shell 的子级。说得通。尽管我注意到有时 A 的父级是/sbin/upstart --user
(至少在 ubuntu 16.10 中)。很奇怪,但还好。
但是当我在后台运行管道(即./pipecommand.sh A | ./pipecommand.sh B &
)时,我得到以下输出:
S: PID 11 PPID 22
B: PID 33 PPID 55
A: PID 44 PPID 33
好的,所以 B 的父级是暴发户(PID 55)。但是A的父母是B(或者像以前一样随机暴发户)?这是怎么回事?这是一个错误还是有一些文档解释了为什么会发生这种情况?对于以特定方式处理 SIGCHLD 的程序来说,这似乎尤其糟糕(这就是我遇到这个问题的方式)。
作为比较,bash
A 和 B 中第一种情况是 shell 的子代,第二种情况是 upstart 的子代。这些结果似乎是一致的。
ksh --version
给我:version sh (AT&T Research) 93u+ 2012-08-01
。
答案1
ksh
,如 Bourne shell 或yash
,当非交互式时仅等待管道中最右边的命令。你会注意到:
ksh -c 'sleep 1 | true'
立即返回那里。
在你的情况下,有时,父进程在启动时已经走了A
,并执行 agetppid()
来填充变量,并且你会得到在其父亲死亡后采用该进程的子进程$PPID
的 pid 或子进程。init
这不是一个错误。这是由选择决定的。这样做而不是等待每个管道组件有其优点(例如在大多数情况下提高了性能)和不便,例如像这样的意外行为,或者管道左侧的命令产生的结果是管道中下一个命令的结果脚本利用。