我正在挖掘不同的来源,但找不到对儿童收割的解剖结构的良好描述。这是我想了解的一个简单案例。
$ cat <( sleep 100 & wait ) &
[1] 14247
$ ps ax -O pgid | grep $$
12126 12126 S pts/17 00:00:00 bash
14248 12126 S pts/17 00:00:00 bash
14249 12126 S pts/17 00:00:00 sleep 100
14251 14250 S pts/17 00:00:00 grep --color=auto 12126
$ kill -2 14248
$ ps ax -O pgid | grep $$
12126 12126 S pts/17 00:00:00 bash
14248 12126 Z pts/17 00:00:00 [bash] <defunct>
14249 12126 S pts/17 00:00:00 sleep 100
14255 14254 S pts/17 00:00:00 grep --color=auto 12126
为什么僵尸在等孩子?
你能解释一下这个吗?我是否需要了解 C 并阅读 Bash 源代码才能更广泛地了解这一点,或者是否有任何文档?我已经咨询过:
- 本网站和 Stack Overflow 上的各种链接
- Linux 命令行作者:W.肖茨
man bash
- Bash 参考手册(在 Bash 源代码文档中)
- Bash 初学者指南@tldp.org
- 高级 Bash 脚本指南
GNU bash,版本 4.3.42(1)-release (x86_64-pc-linux-gnu)
Linux 4.4.0-31-generic #50-Ubuntu SMP 7 月 13 日星期三 00:07:12 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
答案1
僵尸没有等待它的孩子。与任何僵尸进程一样,它会一直存在,直到其父进程收集它为止。
您应该显示所有涉及的进程以了解正在发生的情况,并查看 PPID。使用此命令行:
ps -t $(tty) -O ppid,pgid
您要杀死的进程的父进程是cat
.发生的情况是 bashcat <( sleep 100 & wait )
在子 shell 中运行后台命令。由于该子 shell 所做的唯一事情就是设置一些重定向,然后运行外部命令,因此该子 shell 被外部命令替换。以下是概要:
- 原始 bash (12126) 调用在子进程 (14247) 中
fork
执行后台命令。cat <( sleep 100 & wait )
- 子进程 (14247) 调用
pipe
创建一个管道,然后fork
创建一个子进程来运行进程替换sleep 100 & wait
。- 孙子(14248)调用后台
fork
运行。sleep 100
由于孙进程不具有交互性,因此后台进程不会在单独的进程组中运行。然后孙子就等着sleep
退出。
- 孙子(14248)调用后台
- 子进程 (14247) 调用
setpgid
(它是交互式 shell 中的后台作业,因此它有自己的进程组),然后execve
运行cat
. (令我有点惊讶的是,后台进程组中没有发生进程替换。)
- 子进程 (14247) 调用
- 你杀了孙子(14248)。它的父进程正在运行
cat
,它对任何子进程一无所知,也没有业务调用wait
。由于孙子的父母不收割,孙子就像僵尸一样留下来。 - 最终,
cat
退出——要么是因为你杀死了它,要么是因为sleep
返回并关闭了管道,所以cat
看到了它的输入的结尾。此时,僵尸的父进程死亡,因此僵尸被 init 收集,并由 init 获取它。
如果将命令更改为
{ cat <( sleep 100 & wait ); echo done; } &
thencat
在一个单独的进程中运行,而不是在原始 bash 进程的子进程中运行:第一个子进程必须留下来运行echo done
。在这种情况下,如果你杀死了孙子,它不会继续作为僵尸,因为孩子(此时仍在运行 bash)会收获它。
答案2
僵尸没有在等孩子。相反,僵尸是这已经死亡的进程(由其自身死亡,或被杀死 - 如您的示例所示),其代码、数据和堆栈被释放,现在仅包含其退出代码,等待其父级调用以wait(2)
检索它(因此最终干净完全来自进程表的进程条目)
在您的示例中,当 sleep 完成(或被杀死)时,父级将读取退出状态,并收获僵尸。wait(2)
详情请参阅上文。