注销时后台进程是否会收到 SIGHUP?

注销时后台进程是否会收到 SIGHUP?

这是这个问题

我又进行了一些测试;看起来这在物理控制台还是通过 SSH 完成并不重要,而且这不仅仅发生在 SCP 上;我也用 进行了测试cat /dev/zero > /dev/null。行为完全相同:

  • 使用 在后台启动一个进程(或者使用和&启动后将其置于后台);这完成了CTRL-Zbg无需使用nohup
  • 注销。
  • 重新登录。
  • 该进程仍然存在,运行正常,并且现在是 的直接子进程init

如果发送了;我可以确认 SCP 和 CAT 都会立即退出SIGHUP;我使用 进行了测试kill -HUP

因此,SIGHUP 确实看起来像是未发送注销时,至少到后台进程(由于显而易见的原因,无法使用前台进程进行测试)。

这个问题最初发生在我使用 VMware ESX 3.5(基于 RedHat)的服务控制台时,但我能够在 CentOS 5.4 上准确复制它。

问题又来了:注销时,不应该向进程发送 SIGHUP 吗,即使它们在后台运行?为什么不发生这种情况?


编辑

strace按照 Kyle 的回答检查了一下。
正如我所料,这个过程并没有任何从启动 shell 注销时发出信号。使用服务器控制台和通过 SSH 时都会发生这种情况。

答案1

找到答案。

对于 BASH,这取决于huponexitshell 选项,可以使用内置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_BLOCKSIG_SETMASKsleep

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,当我注销时后台进程继续运行。

相关内容