这是这个问题。
我又进行了一些测试;看起来这在物理控制台还是通过 SSH 完成并不重要,而且这不仅仅发生在 SCP 上;我也用 进行了测试cat /dev/zero > /dev/null
。行为完全相同:
- 使用 在后台启动一个进程(或者使用和
&
启动后将其置于后台);这完成了CTRL-Z
bg
无需使用nohup
。 - 注销。
- 重新登录。
- 该进程仍然存在,运行正常,并且现在是 的直接子进程
init
。
如果发送了;我可以确认 SCP 和 CAT 都会立即退出SIGHUP
;我使用 进行了测试kill -HUP
。
因此,SIGHUP 确实看起来像是未发送注销时,至少到后台进程(由于显而易见的原因,无法使用前台进程进行测试)。
这个问题最初发生在我使用 VMware ESX 3.5(基于 RedHat)的服务控制台时,但我能够在 CentOS 5.4 上准确复制它。
问题又来了:注销时,不应该向进程发送 SIGHUP 吗,即使它们在后台运行?为什么不发生这种情况?
编辑
我strace
按照 Kyle 的回答检查了一下。
正如我所料,这个过程并没有任何从启动 shell 注销时发出信号。使用服务器控制台和通过 SSH 时都会发生这种情况。
答案1
找到答案。
对于 BASH,这取决于huponexit
shell 选项,可以使用内置shopt
命令查看和/或设置。
看起来这个选项默认是关闭的,至少在基于 RedHat 的系统上是这样。
更多信息BASH 手册页:
默认情况下,shell 在收到 SIGHUP 后退出。在退出之前,交互式 shell 会将 SIGHUP 重新发送给所有正在运行或已停止的作业。已停止的作业将收到 SIGCONT 以确保它们收到 SIGHUP。要防止 shell 向特定作业发送信号,应使用 disown 内置命令将其从作业表中删除(请参阅下面的 SHELL 内建命令),或使用 disown -h 将其标记为不接收 SIGHUP。
如果已使用 shopt 设置了 huponexit shell 选项,则当交互式登录 shell 退出时,bash 会向所有作业发送 SIGHUP。
答案2
在我的测试中它将被发送 SIGHUP:
外壳1:
[kbrandt@kbrandt-opadmin: ~] ssh localhost
[kbrandt@kbrandt-opadmin: ~] perl -e sleep &
[1] 1121
[kbrandt@kbrandt-opadmin: ~] ps
PID TTY TIME CMD
1034 pts/46 00:00:00 zsh
1121 pts/46 00:00:00 perl
1123 pts/46 00:00:00 ps
外壳2:
strace -e trace=signal -p1121
再次使用 Shell1:
[kbrandt@kbrandt-opadmin: ~] exit
zsh: you have running jobs.
[kbrandt@kbrandt-opadmin: ~] exit
zsh: warning: 1 jobs SIGHUPed
Connection to localhost closed.
再次使用 Shell2:
strace -e trace=signal -p1121
Process 1121 attached - interrupt to quit
pause() = ? ERESTARTNOHAND (To be restarted)
--- SIGHUP (Hangup) @ 0 (0) ---
Process 1121 detached
它为什么还能运行?:
Stevens 所著的《Unix 环境高级编程》第 9.10 节“孤立进程组”介绍了此内容。最相关的部分是:
由于当父进程终止时,进程组就会变为孤立进程,因此 POSIX.1 要求向新变为孤立进程组中的每个停止的进程(就像我们的子进程一样)发送挂断信号(SIGHUP),然后发送继续信号(SIGCONT)。
这会导致子进程在处理完挂断信号后继续运行。挂断信号的默认操作是终止进程,因此我们必须提供一个信号处理程序来捕获该信号。因此,我们希望 sig_hup 函数中的 printf 出现在 pr_ids 函数中的 printf 之前。
答案3
我使用 CentOS 7.1 和 bash 运行了一些测试。请注意,默认情况下,这意味着huponexit
大多数off
测试都是关闭的。
nohup
当你在终端启动一项工作时,你需要如果你关闭该终端而没有干净地退出 shell, 这终端向 bash 发送 SIGHUP 信号给 shell,然后 shell 将其发送给所有子进程。如果您干净地退出 shell(这意味着该作业必须已经在后台,以便您可以exit
在命令提示符下键入或按下 Control-D),则 bash 不会向后台作业发送任何类型的信号。
测试:
1号航站楼
$ echo $$
16779
2 号航站楼
$ strace -e signal -p16779
Process 16779 attached
(关闭1号航站楼,在2号航站楼可见):
--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=16777, si_uid=3000090} ---
rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], 8) = 0
rt_sigprocmask(SIG_SETMASK, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], 8) = 0
rt_sigprocmask(SIG_SETMASK, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], NULL, 8) = 0
rt_sigaction(SIGHUP, {SIG_DFL, [], SA_RESTORER, 0x7f7ace3d9a00}, {0x456880, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x7f7ace3d9a00}, 8) = 0
kill(16779, SIGHUP) = 0
rt_sigreturn() = -1 EINTR (Interrupted system call)
--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=16779, si_uid=3000090} ---
+++ killed by SIGHUP +++
工作doit.sh
:
#!/bin/bash
imhupped() {
echo "HUP" >> /tmp/outfile
}
trap imhupped SIGHUP
for i in $(seq 1 6); do echo out $i >> /tmp/outfile; sleep 5; done
在终端 1 的后台启动它:
1号航站楼
$ ./doit.sh &
[1] 22954
在 Terminal 2 中进行 Strace ;经过几个循环后关闭 Terminal 1 :
2 号航站楼
$ strace -e signal -p22954
Process 22954 attached
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=22980, si_status=0, si_utime=0, si_stime=0} ---
rt_sigreturn() = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f7a5d547a00}, {0x43e4b0, [], SA_RESTORER, 0x7f7a5d547a00}, 8) = 0
...
--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=21685, si_uid=3000090} ---
rt_sigreturn() = -1 EINTR (Interrupted system call)
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_KILLED, si_pid=23017, si_status=SIGHUP, si_utime=0, si_stime=0} ---
rt_sigreturn() = 0
...
端子 3 的输出:
3 号航站楼
out 1
out 2
out 3
HUP
out 4
out 5
out 6
但是,如果您 exit bash
,它只会退出,而不会向子进程发送任何信号。终端将退出,因为它不再有子进程,但当然没有人可以 HUP,因为子进程 shell 已经消失了。您在下面看到的 和SIGINT
是由于shell 中的 。SIG_BLOCK
SIG_SETMASK
sleep
1号航站楼
$ ./doit.sh &
26275
2 号航站楼
$ strace -e signal -p26275
Process 26275 attached
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=26280, si_status=0, si_utime=0, si_stime=0} ---
rt_sigreturn() = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f5edd3a5a00}, {0x43e4b0, [], SA_RESTORER, 0x7f5edd3a5a00}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGINT, {0x43e4b0, [], SA_RESTORER, 0x7f5edd3a5a00}, {SIG_DFL, [], SA_RESTORER, 0x7f5edd3a5a00}, 8) = 0
(..."exit" is typed in bash, notice no new signals sent...)
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=26303, si_status=0, si_utime=0, si_stime=0} ---
rt_sigreturn() = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f5edd3a5a00}, {0x43e4b0, [], SA_RESTORER, 0x7f5edd3a5a00}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGINT, {0x43e4b0, [], SA_RESTORER, 0x7f5edd3a5a00}, {SIG_DFL, [], SA_RESTORER, 0x7f5edd3a5a00}, 8) = 0
端子 3,输出
out 1
out 2
out 3
out 4
out 5
out 6
有趣的是,我设置huponexit
为继续shopt -s huponexit; shopt
(后者要进行审查),然后执行最后一个测试,然后再次bash 没有向后台进程发送信号更有趣的是,我们已经看到 bash做过在从关闭的终端接收到信号后,将信号发送到后台进程。似乎对huponexit
任何方式都没有影响。
我希望这至少能消除有关 bash 的 huppiness 的任何神秘或困惑,关于何时以及如何发送 HUP 信号。至少我的测试对我来说是完全可重现的。我很想知道是否有任何其他设置可能会影响 bash 的行为。
和往常一样,YSMV(您的 Shell 可能会有所不同)。
附录1
当我以 身份运行 shell exec /bin/sh
,然后以 身份运行脚本/bin/sh ./doit.sh &
,然后干净地退出 shell 时,不会向后台作业发送任何信号,并且它将继续运行直至完成。
附录2
当我以 身份运行 shell exec /bin/csh
,然后以 身份运行脚本/bin/sh ./doit.sh &
,然后干净地退出 shell 时,不会向后台作业发送任何信号,并且它将继续运行直至完成。
答案4
我使用 csh,当我注销时后台进程继续运行。