这是这个问题的后续。
我已经运行了更多的testing; 看起来如果在物理控制台或通过SSH完成的话,这并不重要,这也不会发生在SCP中; 我也用cat /dev/zero > /dev/null
testing它。 行为是完全一样的:
&
(或者在使用CTRL-Z
和bg
开始后,把它放在后台)在后台启动一个进程; 这是不使用nohup
完成的。 init
的直接子init
。 如果发送SIGHUP
,我可以确认SCP和CAT都立即退出; 我用kill -HUP
testing了这个。
所以,看起来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而不干净地退出shell , terminal发送一个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。 下面看到的SIGINT
, SIG_BLOCK
和SIG_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和后台进程在我注销时继续运行。