【问题标题】:Detect death of parent process检测父进程的死亡
【发布时间】:2012-08-30 09:15:10
【问题描述】:

如何在 Linux 操作系统中检测父进程死亡?

如果父进程名为fork(),则创建子进程。在父进程中,我可以使用系统调用wait() 等待终止的子进程,并获取其状态。

但是,我找不到有关子进程如何检测父进程死亡的信息?

【问题讨论】:

标签: c linux


【解决方案1】:

您可以通过调用getppid() 获取父进程ID,然后通过kill() 发送信号0。返回码 0 表示该进程仍处于活动状态。

正如@Ariel 所提到的,getppid() 将返回原始父进程的 pid 或 init 的 pid,即 pid 1。因此,您必须在启动时或稍后调用 getppid() 来存储父进程 pid检查你的父母是否有 pid 1。

根据 Linux 上的this answer,您还可以通过prctl()PR_SET_PDEATHSIG 选项和自选信号检测父母的死亡。

【讨论】:

  • 所有进程都有父进程,不可能没有父进程(即kill 永远不会为来自getppid 的pid 返回0)。如果进程的父进程死亡,它通常会杀死子进程。如果不是,那么该进程将被具有 pid 1 的 init 采用 - 所以检查一下。
  • @Ariel 没错,要么检查 pid 1,要么在启动时直接调用 getppid() 并存储父 pid,以便稍后通过 kill() 进行检查。
  • 您实际上并不需要 kill - 只需再次调用 getppid 并查看它是否发生了变化(尽管那里存在潜在的竞争条件,所以还要检查 1)。
  • 我不认为,不断检查不断变化的 ppid 这是个好主意。我需要在异步操作。我认为,使用 prctl() 是最佳解决方案。
  • 试过了,但对我来说,在 Linux 上 getppid() 如果父级死亡,则返回 >1。我不认为有些人可以假设这种行为。我决定检查 id 是否发生变化。
【解决方案2】:

如果父进程和子进程的生命周期都在您的控制之下,最便携的方法是与父进程共享管道或套接字的一半。

  1. 在 fork 之前,打开一个 pipe() 或 socketpair()。
  2. 分叉后,
    1. 在父级中,关闭管道的读取端或第一个套接字。
    2. 在子级中,关闭管道的写入端或第二个套接字。
  3. 在父级中,将剩余的文件描述符隐藏起来并忘记它。
  4. 在子级中,使用任何多路复用 IO 方法(select、poll 等)来测试描述符的可读性
  5. 如果描述符变得可读,则几乎可以肯定父级已死,或者某些罕见的错误导致了杂散写入,您可以通过调用 read() 来检查。如果父级真的死了,read() 将返回 0 个字节。

这种方法的优点是它完全避免了信号,这是 UNIX 中最难掌握的机制之一,并提供了一个可以轻松与网络多路复用器或 GUI 事件循环集成的可等待描述符。

【讨论】:

  • 这不仅避免了信号。当您无法使用 getppid() 来确定父进程是否已死亡时,也可以使用它。例如,如果您正在创建一个看门狗进程。看门狗进程将自己守护进程分叉子 1,然后子 1 分叉子 2,然后子 1 死亡。现在孩子 2 的父进程 ID = 1。所以 getppid() 在那里不起作用。唯一的方法是使用管道或套接字来确定主进程是否已经死亡。
【解决方案3】:

在我的 Ubuntu 16.04.1 LTS 中,getppid() 在父级杀死后不返回“1”但进程的 id 为“/sbin/upstart --user”,因此检查 getppid() == 1 将不起作用并且 getppid()应在子项启动时保存并稍后进行比较。

【讨论】: