我正在尝试帮助 AskUbuntu 的用户解决以下问题,
语境:-
我正在 bash 中运行一个进程,并
&
在后台模式下附加 ie。该进程有一个while(1)
即它将永远运行。我的 bash 是父进程,我的进程是子进程。我想了解当父进程终止时内核如何处理子进程。观察结果:-
- 如果我退出终端(使用
exit
终端命令),则/lib/systemd/systemd --user
成为子进程的父进程kill
如果我从另一个终端终止终端进程(使用),结果与上面相同- 如果我使用十字图标关闭终端,子进程也会终止
问题:-
关闭与杀死/退出有何不同?
我已经写了一个答案,但觉得我不太明白发生了什么,我认为在 Unix & Linux 上提供帮助的几个人比我更了解并且可以解释发生了什么。
因此,请通过插入并写下答案来帮助我们理解(代替我在 AskUbuntu 上的尝试)。
答案1
1 的答案是systemd --user
采用孤儿(即扮演 pid 1 = init 的角色),因为它通过prctl(PR_SET_CHILD_SUBREAPER, 1)
在生成其子级之前调用而成为“子收割者”。这是一个 Linux 扩展。
3 的答案是,这取决于终端仿真器。它们的工作方式并不相同。
但一般来说,1,2和3之间的区别在于,在后一种情况下巴什将收到一个SIGHUP
信号 [1] 并将其重新发送给其子进程,导致后台进程终止:
shell [即 bash] 在收到
SIGHUP
.退出前,交互的shell 将 重新发送SIGHUP
到所有正在运行或已停止的作业。发送停止的作业SIGCONT
以确保它们收到SIGHUP
.
这是一个bash 扩展;并非所有 shell 都重新发送SIGHUP
信号。具体来说,dash(/bin/sh
Ubuntu 上的)不会做这样的事情。
SIGHUP
但是,即使在 1 的情况下,如果后台进程在其进程组中有一个已停止的进程(使用 模拟终端模拟器script
),它也可以获得信号:
$ script -q /dev/null -c /bin/bash
$ sh -c 'sleep 1 & kill -STOP $!; echo $$; while sleep 1; do :; done' &
[1] 3317
$ 3317
$ exit
exit
$ ps 3317
PID TTY STAT TIME COMMAND
# 3317 is dead.
这停止了(不是睡眠)sleep
进程已删除其父进程。如果你遗漏了这个sleep 1 & kill -STOP $!
部分,它就会存活下来。
与前一个不同,这是操作系统的标准功能,而不是 bash 或其他 shell 特有的功能。
[1] bash 将收到一个SIGHUP
信号,因为它位于前台进程组中,并且终端仿真器已经破坏了伪终端(导致内核发送SIGHUP
)或本身显式发送了SIGHUP
。
答案2
子进程必须始终有一个父进程(并且每个进程都有一个父进程 PID)。
在前两种情况下,您会杀死父进程,因此您的进程将具有无效的父 ID。 Unix(和Posix)规定init
将采用这样的进程(因此PID为1的进程,现在通常是systemd),并且这样的进程会收到一个信号(因此它知道它应该采用新的子进程,或者可能只是杀死它) 。这也是init
(因此 systemd)很重要的原因:它可以查看重要进程是否被杀死或孤立,并且可以采取行动)。
第三个案例就更有趣了。注意:在这种情况下,您不会杀死终端,您只是告诉终端退出,这样终端就有时间清理它的东西。
所以现在我们有其他类型的流程,控制组和作业控制。复杂的主题:终端将停止终端部分(输入处理和如何绘制字符),但是终端(现在通常是 pasudoterminal,并且肯定是 x 中的伪终端)将发送SIGHUP
到此类进程,告诉它们另一端挂断。通常这相当于杀死此类控制组中的所有进程。程序可能会告诉终端忽略该信号,或处理此类信号以便更好地退出(通常是重置屏幕)。 (nohup
实用程序可用于在 SIGHUP 信号的情况下保持进程运行)。
所以区别是: 1 和 2 杀死不能向子进程发送 SIGHUP 的父进程,因此 PID 1 会照顾它们(注意:如果 stdin 和 stdout 不可用,它可能会杀死它,但这取决于父进程)。 3 终端很好的退出,并通知孩子们对方挂断了,一定要顺利处理。