kill -s SIGTERM仅终止父进程和一级子进程的原因及行为疑问
实验场景与问题
实验脚本
parent.sh:
#!/bin/bash /home/oracle/child.sh & sleep infinity
child.sh:
#!/bin/bash sleep infinity
启动命令
su oracle -c parent.sh &
进程树
[root@source ~]# ps -ef | grep "/home/oracle" root 14129 1171 0 12:39 pts/1 00:00:00 su oracle -c /home/oracle/parent.sh oracle 14130 14129 0 12:39 ? 00:00:00 /bin/bash /home/oracle/parent.sh oracle 14131 14130 0 12:39 ? 00:00:00 /bin/bash /home/oracle/child.sh
执行kill -s SIGTERM 14129后,进程14129和14130立即终止,但14131长期存活并被重新父化至进程1,由此产生两个疑问:
- 若kill命令不会终止子进程,为何发送SIGTERM给14129时14130会被终止?
- 若kill命令可终止子进程,为何仅终止一级子进程?该行为是否有保障?
解答
问题1:为何14130会被终止?
kill命令本身不会主动递归终止目标进程的子进程,14130被终止的原因是它是su oracle -c parent.sh(进程14129)的直接子进程,且su进程在收到SIGTERM退出时,会向自己启动的子进程发送终止信号,这是su这类程序的默认实现行为。
具体来说,su oracle -c parent.sh启动后会fork出子进程(14130)执行bash运行parent.sh,当14129收到SIGTERM终止时,会在退出前向14130发送SIGHUP或SIGTERM信号,导致14130随之终止。
问题2:为何仅终止一级子进程,行为是否有保障?
kill命令本身没有递归终止子进程的功能,14131未被终止的核心原因是:parent.sh进程(14130)在收到信号终止时,没有向它的后台子进程14131发送终止信号。
parent.sh用&将child.sh放到后台运行,此时child.sh是它的后台子进程。bash的默认逻辑是收到终止信号时,不会主动向后台子进程发送信号,后台进程会成为孤儿进程,随后被进程1接管并继续存活。
这种“仅终止一级子进程”的行为没有通用性保障:su是否向子进程发信号、bash如何处理后台子进程,都属于程序自身的实现细节,而非kill命令的标准特性。不同版本的su、bash可能有不同处理逻辑,不能依赖该行为实现进程树的完整清理。
如果需要递归终止整个进程树,建议使用专门工具(如pkill -P <PID>),或在父进程中通过trap捕获信号,主动向所有子进程发送终止指令。
内容的提问来源于stack exchange,提问作者Prabhu

