【问题标题】:Is it possible to adopt a process?是否可以采用流程?
【发布时间】:2012-05-18 04:33:22
【问题描述】:

进程 A fork()s 进程 B。

进程 A 死亡,因此 init 采用 B。

看门狗创建进程 C。

C 有没有可能从init 采用 B?


更新

或者如果 C 是在 A 死之前创建的,而 init 没有成为 B 的中间父级,甚至可以让 C 直接采用 B(当 A 死时)?


更新 1:

另外,如果有任何 cmets 解释为什么有可能采用我所描述的方式将是一件坏事或难以实施,我将不胜感激。


Update-2 - 用例(父子进程指代进程):

我有一个应用程序使用父母来管理一大群孩子,这些孩子依赖于父母的管理设施。为了完成其工作,父母依赖于收到孩子终止的通知,这是通过接收相关的SIGCHLD 信号来完成的。

如果父母本身因某种事故(包括段错误)而死亡,我需要重新启动整个“家庭”,因为现在不可能在孩子终止时触发某些事情(这也可能是由于段错误)。

在这种情况下,我需要关闭所有孩子并重新启动整个系统。

避免这种情况的一种可能方法是建立一个备用进程,它可以接管死去的父母的角色...... - 如果它可以再次接收到继子的SIGCHLD 信号!

【问题讨论】:

  • 如果孩子们继承了父母制作的管道的“读取”端,他们可能会让自己失望。 'read' 端将在父母去世时选择可读(对于 EOF),这是每个孩子可以捕获并做出反应的 IO 事件。
  • 这个问题是,我不想让孩子们失望。我希望有可能通过继父进程替换死去的父母(就能够接收他们的 SIGCHLD 而言,以防他们终止)。 @pilcrow
  • 在我上面的评论中,这个“...收到他们的 SIGCHLD ...”应该是“...收到他们孩子的 SIGCHLD ...”。

标签: c unix parent children process-reaper


【解决方案1】:

我不知道有什么好的方法可以做到这一点,但拥有它的一个原因是运行的进程可以独立运行或向父进程添加功能。收养将作为事件的结果发生,由(尚未)孩子知道,但父母不知道。即将出生的孩子会向父母发送信号。父母会收养(或不收养)孩子。一旦成为父进程的一部分,父/子进程将能够对事件做出反应,而在单独站立时,它们都无法对事件做出反应。

这种对接行为可以编码到应用程序中,但我不知道如何实时执行。还有其他方法可以实现相同的功能。可以接受对接孩子的父母可以以父母以前不知道的新颖方式扩展其功能。

【讨论】:

    【解决方案2】:

    不,绝对不可能。如果没有一些讨厌的竞争条件,它也无法实现。制作这些 API 的 POSIX 人员永远不会创建具有固有竞争条件的东西,因此即使您不介意,您的内核也不会很快得到它。

    一个问题是 pid 会被重复使用(它们是稀缺资源!),而且您也无法获得句柄或锁定;这只是一个数字。因此,比如说,在您的代码中的某处,您有一个变量,您可以在其中放置要重新设置的进程的 pid。然后你打电话给make_this_a_child_of_me(thepid)。那时会发生什么?与此同时,其他进程可能已经退出,thepid 更改为引用其他进程!哎呀。如果不对 unix 处理进程的方式进行大规模重组,就无法提供make_this_a_child_of_me API。

    请注意,对子 pid 进行waiting 的整个处理正是为了防止这个问题:进程表中仍然存在僵尸进程,以防止其 pid 被重用。然后,父进程可以通过它的 pid 引用它的子进程,确信进程不会退出并让子进程 pid 重用。如果子进程退出,它的 pid 将被保留,直到父进程捕获 SIGCHLD 或等待它。一旦进程被收割,它的 pid 立即可供其他程序在它们 fork 时开始使用,但父进程保证已经知道它。

    对更新的响应:考虑一个更复杂的方案,其中进程被重新设置为它们的下一个祖先。显然,这不可能在所有情况下都这样做,因为您经常想要一种与孩子断绝关系的方式,以确保您避免僵尸。 init 很好地完成了这个角色。因此,流程必须通过某种方式来指定它打算收养或不收养其孙辈(或更低)。这种设计的问题与第一种情况完全相同:您仍然获得竞争条件。

    如果再次由 pid 完成,则祖父母将自己暴露于竞争条件:只有父母能够获得 pid,因此只有父母真正知道 pid 与哪个进程一起使用。因为祖父母不能收割,所以不能确定孙进程没有改变它打算采用的进程(或不承认,取决于假设的 API 的工作方式)。请记住,在负载很重的机器上,没有什么可以阻止进程在几分钟内从 CPU 中取出,而整个负载可能在这段时间内发生了变化!不理想,但 POSIX 必须考虑到这一点。

    最后,假设这个 API 不能通过 pid 工作,而只是一般地说,“将所有孙子发送给我”或“将它们发送给 init”。如果在子进程生成后调用它,那么您会像以前一样获得竞争条件。如果它之前被调用过,那么整个事情就没用了:您应该能够稍微重构您的应用程序以获得相同的行为。也就是说,如果您在开始生成子进程之前就知道谁应该是谁的父进程,那么为什么不能一开始就以正确的方式创建它们呢?管道和 IPC 确实能够完成所有需要的工作。

    【讨论】:

    • 感谢您的解释。我虽然明白简单地接管一个过程的父母是不可行的。但是对于第二种方法(在我的问题中在Update 下提到),可能存在一种可能性,方法是将现有进程注册为备用进程以在另一个进程死亡的情况下执行到位(关于亲子关系),以便其子进程随后不会被init 采用,而是被(先前注册的)备用进程采用。
    • 再次感谢您交流您的想法。关于重新抚养祖父母,我肯定看到这不能成为默认行为,因为许多现有的实现(和模式)依赖于失去父母的孩子被 init 收养。
    • 由于init 一个进程,它确实领养子(由内核执行),所以为什么不允许内核为这些孤儿分配一些其他过程,但 init。我确实在考虑您在段落中提到的以“将所有孙子发送给我”或“将它们发送给init”开头的方法。有关我面临的用例的详细信息,请参阅我的问题中的 Update-2。
    • 好点。在 Linux 内核中,虽然子代和父代 task_struct 保持彼此的指针,所以从技术上讲,当父母去世时,祖父母可以继承孩子。
    • 请注意,例如linux(从内核 3.4 开始)除了 "re-parent to init" 之外还有另一种方法来重新父进程,请参阅 stackoverflow.com/questions/6476452/… ,(但仍然无法再次从 init 采用进程)
    【解决方案3】:

    不,您无法按照您描述的方式强制执行 Reparenting。

    【讨论】:

    • 虽然我很难接受你写的东西,但我接受你的回答...... ;-) - 无论如何,我想知道是否有充分的理由不提供这种可能性。或者只是因为没有人需要它,所以没有人实施它(还)? @als
    • @alk:如果你觉得很难接受,就不要接受,让 q 成为或添加赏金。
    • 你是对的。我不会接受你的回答,因为这可能会促使有人对这个问题发表评论。 @als
    猜你喜欢
    • 2011-01-28
    • 1970-01-01
    • 1970-01-01
    • 2011-04-21
    • 2011-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多