谁应该杀死进程?
通常情况下,前台和后台进程会在不同的情况下被内核或 shell 发送的 SIGHUP
信号杀死。
内核何时发送SIGHUP
信号?
内核会向控制进程发送SIGHUP
信号:
- 对于真实(硬件)终端:在终端驱动程序中检测到断开连接,例如在调制解调器线路上挂起时;
- 对于伪终端(pty):当关闭最后一个引用主端的描述符时,例如关闭终端窗口。
内核会向其他进程组发送SIGHUP
信号:
- 当控制进程终止时,会向前台进程组发送信号;
- 当孤立的进程组变为孤儿进程组且具有停止成员时,会向孤儿进程组发送信号。
控制进程是建立与控制终端的连接的会话领导者。
通常,控制进程是您的shell。因此,总结如下:
- 当真实或伪终端被断开/关闭时,内核向shell发送
SIGHUP
;
- 当shell终止时,内核会向前台进程组发送
SIGHUP
;
- 如果孤立的进程组包含已停止的进程,则内核会向其发送
SIGHUP
。
请注意,如果后台进程组中不包含已停止的进程,则内核不会向其发送SIGHUP
。
什么时候bash
会发送SIGHUP
信号?
Bash会向所有作业(前台和后台)发送SIGHUP
信号:
- 当它接收到
SIGHUP
信号,且它是一个交互式shell(并且编译时启用了作业控制支持);
- 当它退出时,它是一个交互式登录shell,并且设置了
huponexit
选项(并且编译时启用了作业控制支持)。
更多详情请点击这里。
注意:
bash
不会向使用disown
从作业列表中删除的作业发送SIGHUP
信号;
- 使用
nohup
启动的进程会忽略SIGHUP
信号。
更多详情请点击这里。
其他shell有什么不同?
通常,shell会传播SIGHUP
信号。在正常退出时生成SIGHUP
信号较少见。
Telnet或SSH
在使用Telnet或SSH时,当连接关闭(例如当您关闭PC上的telnet
窗口时),应该发生以下情况:
- 客户端被终止;
- 服务器检测到客户端连接已关闭;
- 服务器关闭pty的主端;
- 内核检测到主pty已关闭并向
bash
发送SIGHUP
;
bash
接收到SIGHUP
,向所有作业发送SIGHUP
并终止;
- 每个作业都会接收到
SIGHUP
并终止。
问题
我可以使用busybox
或dropbear
SSH服务器中的bash
和telnetd
重现您的问题:有时候,当客户端连接关闭时,后台作业不会接收到SIGHUP
(并且不会终止)。
似乎在服务器(telnetd
或dropbear
)关闭pty的主侧时发生了一个竞争条件:
- 通常情况下,
bash
会收到SIGHUP
并立即杀死后台作业(按预期),然后终止;
- 但有时,
bash
在处理SIGHUP
之前就在pty的从侧检测到EOF
。
当bash
检测到EOF
时,默认情况下会立即终止而不发送SIGHUP
。而后台作业仍在运行!
解决方案
可以配置 bash
在正常退出(包括EOF
)时发送SIGHUP
信号:
确保以登录shell的方式启动bash
。据我所知,huponexit
只对登录shell有效。
可以通过-l
选项或者argv[0]
中的前导连字符来启用登录shell。您可以配置telnetd
运行/bin/bash -l
或更好的是运行/bin/login
,它会以登录shell模式调用/bin/sh
。
例如:
telnetd -l /bin/login
启用huponexit
选项。
例如:
shopt -s huponexit
每次在bash
会话中键入此命令,或将其添加到.bashrc
或/etc/profile
中。
为什么会出现这场竞争?
bash
只有在安全时才解除信号阻塞,当某些代码区段不能被信号处理程序安全中断时则会阻塞它们。
这样的 临界区段 会不时地调用 中断点,如果在执行临界区段时接收到了信号,则其处理程序将被延迟到下一个中断点发生或退出临界区段时才执行。
你可以从源代码中的 quit.h
开始查找。
因此,在我们的情况下,似乎 bash
有时会在临界区段中收到 SIGHUP
。 SIGHUP
处理程序的执行被延迟,bash
在退出临界区段或调用下一个中断点之前读取 EOF
并终止。
参考资料
- 官方Glibc手册中的"作业控制"章节。
- "The Linux Programming Interface"一书中的第34章"进程组、会话和作业控制"。
trap
进行了一些本地测试。Bash 接收到了 SIGCLD 和 SIGCONT 以及 SIGHUP。为什么?我们为什么需要确保 bash 作为登录 shell 启动
? - Amoshuponexit
只对登录shell有效,如果我没记错的话 https://dev59.com/YWEi5IYBdhLWcg3wbrzS#21294799 "如果使用shopt设置了huponexit shell选项,则当交互式登录shell退出时,bash会向所有作业发送SIGHUP信号。" 我会更新答案。 - gavvhttps://paste.wentropy.com/zAcW
- Amos