后台进程在注销时是否获得SIGHUP?

这是这个问题的后续。

我已经运行了更多的testing; 看起来如果在物理控制台或通过SSH完成的话,这并不重要,这也不会发生在SCP中; 我也用cat /dev/zero > /dev/nulltesting它。 行为是完全一样的:

  • 使用& (或者在使用CTRL-Zbg开始后,把它放在后台)在后台启动一个进程; 这是不使用nohup完成的。
  • 注销。
  • 再次login。
  • 这个过程仍然在那里,愉快地运行,现在是init的直接子init

如果发送SIGHUP ,我可以确认SCP和CAT都立即退出; 我用kill -HUPtesting了这个。

所以,看起来SIGHUP在注销时不会发送 ,至less是后台进程(由于显而易见的原因,不能用前台进行testing)。

最初发生在我使用VMware ESX 3.5服务控制台(基于RedHat)的情况下,但是我能够在CentOS 5.4上完全复制它。

问题是,SIGHUP不应该在注销时被发送到进程,即使它们在后台运行。 为什么这不会发生?


编辑

按照Kyle的回答,我用strace进行了检查。
正如我期待的那样,从启动的shell中注销时,进程没有任何信号。 这在使用服务器的控制台和通过SSH时都会发生。

find答案。

对于BASH,这取决于huponexit shell选项,可以使用内置的shopt命令查看和/或设置huponexit shell选项。

看起来这个选项默认是closures的,至less在基于RedHat的系统上。

有关BASH手册页的更多信息:

收到SIGHUP后,shell会默认退出。 在退出之前,交互式shell将SIGHUP重新发送到所有正在运行或停止的作业。 停止的工作发送SIGCONT,以确保他们收到SIGHUP。 为了防止shell将信号发送到特定的作业,应该使用disown内build函数(请参阅下面的SHELL BUILTIN COMMANDS)将其从作业表中删除,或者使用disown -h标记为不接收SIGHUP。

如果已经使用shopt设置了huponexit shell选项,则当交互式loginshell退出时,bash将向所有作业发送SIGHUP。

它会在我的testing中发送SIGHUP:

抽壳:

 [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 

Shell2:

 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 

为什么它仍然运行?
在Unix环境下的高级编程由Stevens在第9.10节“孤立进程组”中进行了介绍。 最相关的部分是:

由于进程组在父进程终止时是孤立的,因此POSIX.1要求在新的孤立进程组中的每个进程停止(作为我们的subprocess)被发送挂起信号(SIGHUP),然后是继续信号(SIGCONT )。

这会导致孩子在处理完挂断信号后继续。 挂断信号的默认动作是终止进程,所以我们必须提供一个信号处理器来捕获信号。 因此,我们期望sig_hup函数中的printf出现在pr_ids函数的printf之前。

我用CentOS 7.1和bash运行了一些testing。 注意这意味着huponexit默认是off的,而且大部分testing都是closures的。

当你在一个terminal里开始一个工作时,你需要nohup ,因为如果你closures那个terminal而不干净地退出shellterminal发送一个SIGHUP信号给shell,然后发送给所有的孩子。 如果你干净地退出shell,这意味着作业必须已经在后台,所以你可以在命令提示符下inputexit或者命令Control-D,任何信号都不会从bash发送到后台作业。

testing:

1号航站楼

 $ echo $$ 16779 

2号航站楼

 $ strace -e signal -p16779 Process 16779 attached 

(closuresterminal1,见terminal2):

 --- 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号航站楼

 $ ./doit.sh & [1] 22954 

在2号航站楼内搭乘; closuresterminal1几个循环后:

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 

但是,如果你退出bash ,它就退出,根本没有发送任何信号给孩子。 terminal将因为不再有孩子而退出,但当然因为孩子的shell已经没有了,所以没有HUP。 下面看到的SIGINTSIG_BLOCKSIG_BLOCK是由于shell中的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 

terminal3,输出

 out 1 out 2 out 3 out 4 out 5 out 6 

有趣的是,我把huponexit设置huponexit shopt -s huponexit; shopt (后者购买),然后执行最后的testing,并且再次bash没有向后台进程发送信号 。 就像我们已经看到的bash 一样 ,即使是更多的interstingly 发送信号到后台进程,当它从收到它的terminalclosures它的脸。 看起来好像huponexit是一种或另一种方式。

我希望这至less可以消除有关至lessbash的欢乐,关于何时以及如何发送HUP信号的神秘感或混淆。 至less我的testing对我来说是完全可重复的。 我有兴趣知道是否有任何其他设置可能会影响bash的行为。

和往常一样,YSMV(你的壳可能会有所不同)。

附录1

当我运行一个shell作为exec /bin/sh ,然后运行脚本作为/bin/sh ./doit.sh & ,然后退出shell干净,没有信号发送到后台作业,它会继续运行到完成。

附录2

当我运行一个shell作为exec /bin/csh ,然后运行脚本为/bin/sh ./doit.sh & ,然后完全退出shell,没有信号发送到后台作业,并继续运行完成。

我使用csh和后台进程在我注销时继续运行。