是否存在子进程与其父进程一起死亡的 UNIX 变体?

是否存在子进程与其父进程一起死亡的 UNIX 变体?

我研究 Linux 内核行为已经有一段时间了,我一直很清楚:

当一个进程死亡时,它的所有子进程都会返回给该init进程(PID 1),直到它们最终死亡。

然而,最近,一个比我更有内核经验的人告诉我:

当一个进程退出时,它的所有子进程也会死亡(除非您使用NOHUP在这种情况下它们会返回init)。

现在,尽管我不相信这一点,但我仍然编写了一个简单的程序来确保这一点。我知道我不应该依赖时间(sleep)进行测试,因为它完全取决于进程调度,但对于这个简单的情况,我认为这已经足够了。

int main(void){
    printf("Father process spawned (%d).\n", getpid());
    sleep(5);

    if(fork() == 0){
        printf("Child process spawned (%d => %d).\n", getppid(), getpid());
        sleep(15);
        printf("Child process exiting (%d => %d).\n", getppid(), getpid());
        exit(0);
    }

    sleep(5);
    printf(stdout, "Father process exiting (%d).\n", getpid());
    return EXIT_SUCCESS;
}

这是程序的输出,以及ps每次printf对话的相关结果:

$ ./test &
Father process spawned (435).

$ ps -ef | grep test
myuser    435    392   tty1    ./test

Child process spawned (435 => 436).

$ ps -ef | grep test
myuser    435    392   tty1    ./test
myuser    436    435   tty1    ./test

Father process exiting (435).

$ ps -ef | grep test
myuser    436    1     tty1    ./test

Child process exiting (436).

现在,正如您所看到的,这与我的预期完全一致。孤儿进程(436)被返回给init(1),直到它死亡。

但是,是否有任何基于 UNIX 的系统默认情况下不应用此行为?是否存在任何系统,进程的死亡会立即触发其所有子进程的死亡?

答案1

当一个进程退出时,它的所有子进程也会死亡(除非您使用 NOHUP 在这种情况下它们会返回到 init)。

这是错误的。大错特错了。说这句话的人要么是错误的,要么是将特定情况与一般情况混淆了。

进程的死亡有两种方式间接地导致其子女死亡。它们与终端关闭时发生的情况有关。当终端消失时(历史上是因为串行线路由于调制解调器挂断而被切断,现在通常是因为用户关闭了终端仿真器窗口),会出现 SIGHUP 信号已发送控制过程在该终端中运行 - 通常,初始 shell 在该终端中启动。 Shell 通常会通过退出来对此作出反应。在退出之前,用于交互式使用的 shell 会将 HUP 发送到它们启动的每个作业。

从 shell 启动作业会nohup破坏 HUP 信号的第二个源,因为作业将忽略该信号,因此不会在终端消失时被告知终止。中断 HUP 信号从 shell 到作业的传播的其他方法包括使用 shell 的disown内置函数(如果有的话)(该作业从 shell 的作业列表中删除),以及双分叉(shell 启动一个子进程,该子进程又启动一个子进程)它自己的并立即退出;shell 不知道它的孙子)。

同样,在终端中启动的作业死亡不是因为它们的父进程(shell)死亡,而是因为它们的父进程在被告知杀死它们时决定杀死它们。终端中的初始 shell 死亡不是因为其父进程死亡,而是因为其终端消失(这可能是巧合,也可能不是巧合,因为终端是由 shell 的父进程的终端仿真器提供的)。

答案2

当一个进程退出时,它的所有子进程也会死亡(除非您使用 NOHUP 在这种情况下它们会返回到 init)。

如果进程是会话领导者,则这是正确的。当会话领导者死亡时,会向该会话的所有成员发送 SIGHUP。实际上,这意味着它的孩子及其后代。

进程通过调用使自己成为会话领导者setsid。贝壳就用这个。

答案3

我在答案中遗漏了一点与垂死的父母略有相关的内容:当一个进程在不再有读取进程的管道上写入时,它会收到一个 SIGPIPE。 SIGPIPE 的标准操作是终止。

这确实会导致进程死亡。事实上,这是程序yes终止的标准方式。

如果我执行

(yes;echo $? >&2)|head -10

在我的系统上,答案是

y
y
y
y
y
y
y
y
y
y
141

141 确实是 128+SIGPIPE:

   SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                 readers

man 7 signal

答案4

所以上面的海报所说的是,孩子们不会死,父母会杀死他们(或向他们发送终止信号)。因此,如果您对父级进行编程以(1)保留其所有子级的记录,并且(2)向其所有子级发送信号,那么您就可以得到您所要求的。

这就是 Shell 所做的事情,也应该是您的父进程所做的事情。可能有必要捕获父级中的 HUP 信号,以便您仍然有足够的控制权来杀死子级。

相关内容