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

11 bash process signals zombie-process

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

$ 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
Run Code Online (Sandbox Code Playgroud)

为什么僵尸在等孩子?

你能解释一下这个吗?我是否需要了解 C 并阅读 Bash 源代码才能对此有更广泛的了解,或者是否有任何文档?我已经咨询过:

GNU bash,版本 4.3.42(1)-release (x86_64-pc-linux-gnu)

Linux 4.4.0-31-generic #50-Ubuntu SMP Wed Jul 13 00:07:12 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

Gil*_*il' 17

僵尸不是在等待它的孩子。像任何僵尸进程一样,它会一直存在,直到它的父进程收集它。

您应该显示所有涉及的进程以了解正在发生的事情,并查看 PPID。使用这个命令行:

ps -t $(tty) -O ppid,pgid
Run Code Online (Sandbox Code Playgroud)

您要杀死的进程的父进程是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; } &
Run Code Online (Sandbox Code Playgroud)

然后cat在一个单独的进程中运行,而不是在原始 bash 进程的子进程中运行:第一个子进程必须留在后面才能运行echo done。在这种情况下,如果您杀死孙子,它不会像僵尸一样继续存在,因为子子(此时仍在运行 bash)会收获它。

另请参阅linux 如何处理僵尸进程僵尸可以有孤儿吗?孤儿们会因为收割丧尸而感到不安吗?


Mat*_*lis 6

僵尸不是在等孩子。相反,僵尸是已经死亡进程(被它自己杀死了,或者被杀死了——就像你的例子一样),它的代码、数据和堆栈被释放了,现在只包含它的退出代码,等待它的父级调用wait(2)来检索它(从而最终从进程表中完全清除进程条目)

在您的示例中,当睡眠结束(或被杀死)时,父母将读取退出状态,并收获僵尸。详情见上文wait(2)