在 Linux 系统上,bash 内置命令disown
可用于从当前会话中删除作业。 bash 是如何实现这个功能的呢?这里使用了setsid()
,如果使用了,那么bash是如何触发子进程调用的setsid()
呢?它使用信号吗?
答案1
没有disown
。不是从当前会话 [1] 中删除作业,不会将其与终端分离,并且不会影响当会话领导者退出或控制终端被拆除时内核将发送哪些信号。
disown
只适用于 bash 自己的作业表,只改变 bash 关于它所控制的作业的想法,并且只影响 bash 自己的行为,即它将重新发送SIGHUP
bash 进程接收的作业。重新SIGHUP
发送是 [2] 的一个额外功能bash
,标准不要求,并且与操作系统提供的作业控制无关。
您可以通过一个简单的示例来查看它,我在其中script(1)
创建一个 pty 并在其中运行一个交互式 shell 会话:
$ script /dev/null -qc bash
$ sh -c 'sleep 555 & sleep .1; kill -STOP $!; trap "echo hupped!" HUP; sleep 666' &
[1] 3837
$ disown -a
$ jobs
# no jobs known to bash
$ pgrep -as0
# show all processes from the current session
3836 bash
3837 sh -c sleep 555 & sleep .1; kill -STOP $!; trap "echo hupped!" HUP; sleep 666
3838 sleep 555
3841 sleep 666
$ kill -HUP $$
# seppuku the session leader
Hangup
hupped!
这里内核SIGHUP
向后台进程组(=作业)发送一个信号,因为其中一个进程已停止,并且放弃它会不是防止这种情况发生。
来自的所有进程sh -c '...'
都是同一作业的一部分,包括“后台” sleep &
;默认情况下,shell 脚本不进行作业控制。
如果没有停止后台进程组的成员,则SIGHUP
发送 no :
$ script /dev/null -qc bash
$ sh -c 'sleep 555 & trap "echo hupped!" HUP; sleep 666' &
[1] 3270
$ disown -a
$ kill -HUP $$
# sleep 555, 666 and sh -c are still running
最后,bash 会SIGHUP
向其表中的所有作业发送 a (仅那些由自身启动且未被拒绝的作业),无论 bash 是否是会话领导者,或者作业是否正在运行、停止等:
$ bash
$ sh -c 'sleep 555 & trap "echo hupped!" HUP; sleep 666' &
[1] 3413
$ kill -HUP $$
Hangup
hupped!
Hangup
[1] 无论如何这是不可能做到的;setsid()
只能将一个进程创建为一个新会话不是作为流程组领导者,无法将整个作业移至新的或现有的会话中。
[2] 记录在 bash 的联机帮助页中:
shell 在收到
SIGHUP
.在退出之前,交互式 shell 会重新SIGHUP
向所有正在运行或已停止的作业发送 。发送停止的作业SIGCONT
以确保它们收到SIGHUP
.为了防止 shell 将信号发送到特定作业,应使用内置命令将其从作业表中删除 (请参阅下面的 shell 内置命令)或 使用disown
标记为不接收。SIGHUP
disown -h
还有shopt -s huponexit
一个原因是登录bash shell 在退出时向其作业发送 a HUP
(无论是否由于信号),这再次以令人困惑的方式与操作系统的标准作业控制功能重叠。