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


21

这是该问题的后续措施。

我还要进行一些测试;看起来这是在物理控制台还是通过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的回答,我检查了。
正如我所期望的,从启动它的外壳注销时,该过程没有任何信号。在使用服务器控制台和通过SSH时都会发生这种情况。


如果在CentOS 7.1上使用Bash,一个简单的shell脚本循环如果留在前台会得到一个SIGHUP,但是终端被杀死了。来自另一个终端的strace显示: --- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=10676, si_uid=3000090} --- rt_sigreturn() = -1 EINTR (Interrupted system call) rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
Mike S

后台脚本也是如此。请注意,在循环等待睡眠时,终端处于关闭状态。该炮弹未退出:--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=13944, si_uid=3000090} --- +++ killed by SIGHUP +++
Mike S

查看我的答案进行测试。有趣的是,由于,我没有看到任何行为变化huponexit
Mike S

Answers:


26

找到答案。

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

看起来该选项默认情况下是关闭的,至少在基于RedHat的系统上是这样。

BASH手册页上的更多信息:

默认情况下,在收到SIGHUP时,shell退出。退出之前,交互式外壳程序会将SIGHUP重新发送给所有正在运行或已停止的作业。已停止的作业将发送给SIGCONT,以确保它们收到了SIGHUP。为了防止外壳将信号发送到特定作业,应使用内置的disown将其从作业表中删除(请参阅下面的SHELL BUILTIN COMMANDS),或使用disown -h标记为不接收SIGHUP。

如果已经用shopt设置了huponexit shell选项,则在退出交互式登录shell时,bash会向所有作业发送SIGHUP。


4
已验证。当我执行“退出”,“注销”或CTL-D时,子proc(作业)将不会收到提示(root用户和reg用户)。但是,当我执行“ kill -HUP $$”来杀死bash的当前实例时,子进程DID会收到一声叹息。然后,我设置了huponexit,并且子进程在退出时确实收到了SIGHUP。
CarpeNoctem'2

3

它会在我的测试中发送给SIGHUP:

Shell1:

[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

为什么它仍然可以运行?:
史蒂文斯(Stevens)在Unix环境中的高级编程在9.10节:孤立的进程组下对此进行了介绍。最相关的部分是:

由于当父进程终止时进程组成为孤立进程,因此POSIX.1要求向新孤立进程组中停止的每个进程(就像我们的孩子一样)发送挂断信号(SIGHUP),然后发送继续信号(SIGCONT) )。

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


但是您在此处明确发送了一个SIGHUP;我说的是当您从启动该过程的Shell注销时会发生什么。
Massimo 2010年

尽管我收到有关作业的警告,但键入exit时的结果相同,但随后再次键入exit。我用ZSH进行了测试。
凯尔·布​​兰特

我正在使用BASH,这可能取决于外壳。但是BASH 在注销时应将SIGHUP发送到子进程...
Massimo 2010年

如果作业停止,Bash显然会发送SIGCONT,但我确认如果作业没有停止,它不会发送任何信息。
凯尔·布​​兰特

在CentOS 7.1上使用Bash,我在另一个窗口中停止了发送到进程的SIGTERM:1.)启动简单的shell脚本(带有回声和睡眠的循环),2.)Control-Z it,3)将进程跟踪到另一个窗口,4)退出原始终端。它抱怨我有工作,然后退出我的跟踪显示:$ strace -e signal -p1705 Process 1705 attached --- stopped by SIGTSTP --- --- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=791, si_uid=3000090} --- +++ killed by SIGTERM +++ 奇怪,绝对不符合史蒂文斯引述的部分。
Mike S

2

我使用CentOS 7.1和bash进行了一些测试。请注意,这huponexitoff默认设置,在我的大多数测试中均处于关闭状态。

nohup在终端中开始作业时,您需要这样做,因为如果您关闭该终端而没有干净地退出外壳,则终端会向bash发送bash SIGHUP信号,然后将其发送给所有子级。如果您干净地退出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

在2号航站楼中追踪;在几个循环后关闭终端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

但是,如果退出bash,它只会退出而根本不会向孩子发送任何信号。终端将退出,因为它不再有孩子,但是由于子外壳已经不存在,因此当然没有人要HUP。的SIGINTSIG_BLOCKSIG_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

有趣的是,我开始huponexitshopt -s huponexit; shopt后者(后面的商店进行审查),然后执行最后一个测试,然后bash再次没有向后台进程发送任何信号。甚至更有趣的是,正如我们所见,bash 从面对其终端的终端接收到信号后,确实将信号发送到了后台进程。似乎huponexit没有任何一种方向。

我希望这可以消除关于至少bash的狂喜,关于何时以及如何发送HUP信号的任何神秘或困惑。对我来说,至少我的测试是完全可重复的。我想知道是否还有其他设置可能会影响bash的行为。

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

附录1

当我以身份运行shell时,以身份exec /bin/sh运行脚本/bin/sh ./doit.sh &,然后干净地退出外壳,没有信号发送到后台作业,并且它继续运行直至完成。

附录2

当我以身份运行shell时,以身份exec /bin/csh运行脚本/bin/sh ./doit.sh &,然后干净地退出外壳,没有信号发送到后台作业,并且它继续运行直至完成。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.