进程 A fork()
进程 B。
过程 A 死亡,因此init
采用 B。
监视器创建进程 C。
C是否有可能以某种方式从init
中采用B?
更新:
或者,如果C是在A死之前创造的,而init
没有成为B的中间父母,那么甚至有可能让C直接收养B(当A去世时(吗?
更新-1:
我也希望就为什么有可能采用我所描述的流程是一件坏事或难以实施或难以实施的任何评论。
更新-2 - 用例(父项和子项是指进程(:
我有一个应用程序,使用家长来管理一大群孩子,这些孩子依赖于父母的管理设施。为了完成其工作,父母依赖于通过孩子的终止通知,这是通过接收相关的SIGCHLD
信号来完成的。
如果父母本身因某些事故(包括段错误(而死亡,我需要重新启动整个"家庭",因为现在不可能在孩子终止时触发某些东西(也可能是由于段错误(。
在这种情况下,我需要关闭所有子项并进行完整的系统重新启动。
避免这种情况的一种可能方法是建立一个备用流程,可以接管死去的父母的角色......——如果它能再次接收到继子女的SIGCHLD
信号!
不,绝对不可能。如果没有一些讨厌的比赛条件,它也无法实现。制作这些 API 的 POSIX 人员永远不会创建具有固有竞争条件的东西,所以即使你不被打扰,你的内核也不会很快得到它。
一个问题是pid被重用(它们是一种稀缺资源!(,你也无法获得手柄或锁定;它只是一个数字。因此,比如说,在代码中的某个地方,您有一个变量,您可以在其中放置要重定父级的进程的 pid。然后你打电话给make_this_a_child_of_me(thepid)
.那会怎样呢?同时,另一个进程可能已经退出,thepid
更改为引用其他进程!哎呀。如果不对 unix 处理进程的方式进行大规模重组,就不可能有一种方法可以提供make_this_a_child_of_me
API。
请注意,在子 pid 上wait
的整个处理正是为了防止此问题:进程表中仍然存在僵尸进程,以防止其 pid 被重用。然后,父级可以通过其 pid 引用其子项,确信进程不会退出并重用子项 pid。如果子项确实退出,则保留其 pid,直到父项捕获 SIGCHLD 或等待它。一旦进程被收获,它的pid就会立即被其他程序抢走,以便在它们分叉时开始使用,但可以保证父程序已经知道它。
对更新的响应:考虑一个更复杂的方案,其中进程被重新设置为下一个祖先。显然,这不可能在每种情况下都做到,因为你经常想要一种与孩子断绝关系的方法,以确保你避开僵尸。init 很好地履行了这一角色。因此,一个过程必须以某种方式指定它打算收养或不收养其孙子孙女(或更低(。此设计的问题与第一种情况完全相同:您仍然会获得竞争条件。
如果它再次由 pid 完成,那么祖父级将自己暴露在竞争条件中:只有父级能够获得 pid,因此只有父级真正知道 pid 遵循哪个过程。因为祖父级无法收获,所以它无法确定孙子进程是否与它打算采用的进程没有改变(或否认,取决于假设的 API 的工作方式(。请记住,在负载较重的机器上,没有什么能阻止进程从 CPU 中取出几分钟,并且整个负载可能会在这段时间内发生变化!不理想,但POSIX必须考虑到这一点。
最后,假设这个 API 不按 pid 工作,而只是笼统地说,"把所有的孙子都送到我身边"或"把他们送到 init"。如果在生成子进程后调用它,则会像以前一样获得竞争条件。如果之前调用过它,那么整个事情就没有用了:你应该能够稍微重组你的应用程序以获得相同的行为。也就是说,如果你在开始生成子进程之前就知道谁应该是谁的父进程,为什么你不能首先以正确的方式创建它们呢?管道和IPC确实能够完成所有必需的工作。
不,您无法按照所描述的方式强制执行重定父级。
好方法可以做到这一点,但这样做的一个原因是运行的进程可以独立存在或向父进程添加功能。收养将作为事件的结果发生,(尚未(孩子知道,但父母不知道。即将成为孩子的孩子会向父母发出信号。父母会收养(或不收养(孩子。一旦父进程的一部分,父/子进程将能够对事件做出反应,而单独站立时两者都无法对事件做出反应。
这种对接行为可以编码到应用程序中,但我不知道如何实时执行此操作。还有其他方法可以实现相同的功能。可以接受对接子项的父母可以以父母以前不知道的新方式扩展其功能。
虽然原始问题被标记为 unix,但有一种方法可以在 Linux 上实现这一点,因此值得一提。这可以通过使用子收割机工艺来实现。当一个进程的父进程时,它将被最近的子收割者祖先或初始化者采用。因此,在您的情况下,您将通过prctl(PR_SET_CHILD_SUBREAPER)
将进程 C 设置为子收割者并生成进程 A,当进程 A 死亡时,进程 B 将被 C 采用
Linux 上的另一种选择是在单独的 PID 命名空间中生成 C,使其成为 PID 命名空间的初始化进程,因此可以在 A 死亡时采用 A 的子级。