为什么僵尸在等待它的孩子?

为什么僵尸在等待它的孩子?

我正在挖掘不同的来源,但找不到对儿童收割的解剖结构的良好描述。这是我想了解的一个简单案例。

$ 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 源代码才能更广泛地了解这一点,或者是否有任何文档?我已经咨询过:

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退出。
    • 子进程 (14247) 调用setpgid(它是交互式 shell 中的后台作业,因此它有自己的进程组),然后execve运行cat​​. (令我有点惊讶的是,后台进程组中没有发生进程替换。)
  • 你杀了孙子(14248)。它的父进程正在运行cat,它对任何子进程一无所知,也没有业务调用wait。由于孙子的父母不收割,孙子就像僵尸一样留下来。
  • 最终,cat退出——要么是因为你杀死了它,要么是因为sleep返回并关闭了管道,所以cat看到了它的输入的结尾。此时,僵尸的父进程死亡,因此僵尸被 init 收集,并由 init 获取它。

如果将命令更改为

{ cat <( sleep 100 & wait ); echo done; } &

thencat在一个单独的进程中运行,而不是在原始 bash 进程的子进程中运行:第一个子进程必须留下来运行echo done。在这种情况下,如果你杀死了孙子,它不会继续作为僵尸,因为孩子(此时仍在运行 bash)会收获它。

也可以看看linux如何处理僵尸进程僵尸可以有孤儿吗?孤儿们会因为收割僵尸而感到不安吗?

答案2

僵尸没有在等孩子。相反,僵尸是已经死亡的进程(由其自身死亡,或被杀死 - 如您的示例所示),其代码、数据和堆栈被释放,现在仅包含其退出代码,等待其父级调用以wait(2)检索它(因此最终干净完全来自进程表的进程条目)

在您的示例中,当 sleep 完成(或被杀死)时,父级将读取退出状态,并收获僵尸。wait(2)详情请参阅上文。

相关内容