父进程退出时,挂起的子进程会退出

3

我有以下的进程树

test1.sh
  \- test2.sh
    \- sleep 600  

通常情况下,如果我杀死test1.sh进程,子进程test2.shsleep 600将继续运行。但是,如果我通过发送信号(SIGSTOP或SIGTSTP)暂停sleep 600进程,然后杀死test1.sh进程,子进程test2.shsleep 600将会退出。为什么会这样呢?
这是我的测试程序: test1.sh
#!/bin/sh

./test2.sh

test2.sh

#!/bin/sh

sleep 600

测试步骤:

  1. 运行 test1.sh

    $ ./test1.sh

  2. 打开新控制台并暂停子进程。

    $ kill -19 < sleep pid > 或 kill -20 < sleep pid >

  3. 关闭父进程 test1.sh

    $ kill < test1.sh pid >

在第3步之后,你会发现 test2.sh 和 sleep 600 已经退出。

如果我只运行第1步和第3步,忽略第2步,那么 test2.sh 和 sleep 600 进程将无法退出。

有人能解释一下吗?非常感谢。


1
很遗憾,_另一个网站_列表中没有https://unix.stackexchange.com/。 - James Brown
1个回答

1
当你杀死进程test1.sh时,你会让test2.sh变成孤儿进程,因此你需要知道在你的操作系统中孤儿进程会发生什么。
当进程test2.sh正在运行,而它的父进程已经死亡时,操作系统将其移动到init进程并继续执行。因此,即使你已经杀死了test1.shtest2.shsleep进程仍然在运行。
当进程sleep被停止(信号20)并且它的父进程已经死亡时,操作系统会尝试将其移动到init进程。但是,由于该进程已经停止,并且不再有任何tty能够恢复它(因为它的父进程已经死亡),操作系统可能会决定对该进程进行其他处理。在你的情况下,它会使用SIGKILL死掉,以避免在系统中留下许多停止的孤儿进程的问题。由于sleep进程已经退出,test2.sh进程也随之结束。
从GNU手册中得知:
当进程被停止时,除了 SIGKILL 信号和(显然)SIGCONT 信号外,将不再向其传递更多信号,这些信号被标记为待处理,但在进程继续之前不会被传送。SIGKILL 信号总是导致进程终止,不能被阻塞、处理或忽略。你可以忽略 SIGCONT,但如果进程被停止,它总是会使进程继续。向进程发送 SIGCONT 信号会导致该进程的任何挂起的停止信号被丢弃。同样,当进程接收到停止信号时,针对该进程的任何挂起 SIGCONT 信号都会被丢弃。
当孤儿进程组中的进程收到 SIGTSTP、SIGTTIN 或 SIGTTOU 信号并且未处理时,该进程不会停止。停止该进程可能没有什么用处,因为没有 shell 程序能够注意到它停止并允许用户继续执行。相反,发生的情况取决于您使用的操作系统。有些系统可能什么也不做;其他系统可能会传递另一个信号,如 SIGKILL 或 SIGHUP。在 GNU/Hurd 系统上,该进程会以 SIGKILL 死亡,这避免了许多停止的孤儿进程在系统中出现的问题。
顺便说一下,如果你愿意始终杀死它们,你可以在主进程上添加一个trap来捕获信号并正确退出子进程。

1
即使父进程退出,我们能否让挂起的子进程保持运行(挂起)? - kongxx
如果进程由于资源使用或内存被杀死,您可以避免这种情况。但是,由于它是内核策略,我认为您无法更改它。话虽如此,您可以创建一个进程,该进程分叉test1.sh并在被杀死时重新生成它(但执行将重新开始)。 - Cristian Ramon-Cortes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接