为什么关闭终端模拟器窗口会终止 bash 进程,并将 SIGHUP 陷阱更改为不终止?

为什么关闭终端模拟器窗口会终止 bash 进程,并将 SIGHUP 陷阱更改为不终止?

在 lxterminal 终端模拟器窗口的 bash shell 中,我运行

$ trap "echo hello" SIGHUP 
$ kill -s HUP $$
hello
$

然后我关闭终端模拟器窗口。

关闭终端仿真器窗口是否只会导致 SIGHUP 发送到控制进程(即 bash 进程)?

由于 SIGHUP 陷阱不会终止 bash 进程,我希望 bash 进程不会终止,但为什么 bash 进程实际上终止了?

如果我将陷阱更改为""(忽略),也会发生同样的情况。

终端模拟器很重要。在 xterm 窗口中运行的 bash 中,将 trap 设置为""将使 xterm 窗口不可关闭,而将 trap 设置为echo hello仍可以关闭 xterm 窗口。

谢谢。

答案1

[我将忽略不同终端模拟器的实际和可能的行为;完全合理的行为是在窗口关闭 / 时向 pty发送^D( ) ,而不是将其拆除并导致其中运行的进程接收;以下假设VEOFWM_DELETE_WINDOWSIGHUPxterm,在这种情况下,它将SIGHUP向 shell 的进程组发送 ]。

您看到的行为是由于该readline库正在安装自己的信号处理程序。如果您尝试以下操作:

xterm -e bash --noediting

(或dashzshksh代替bash --noediting),然后运行

trap 'echo HUP' HUP

在终端中,窗口将变得不可关闭; shell 将HUP在尝试关闭窗口时按预期打印;强行关闭它(例如使用xkill)将导致 shell 退出并出现EIO错误,这完全是预料之中的,因为 pty 已被拆除。

这是您正在观察的行为的一个更简单的测试用例,不涉及终端模拟器。在终端中运行以下命令:

bash --rcfile <(echo 'trap "echo HUP" HUP')

然后kill -HUP $$将只 print HUP,但是(sleep 1; kill -HUP $$) &(或kill -HUP <pid>从另一个窗口)将导致 shell 打印exit并退出,除非您使用--noediting(= 不要使用 readline)启动它

readline()调用的函数将bash在等待用户输入时安装自己的信号处理程序,并在返回时恢复原始处理程序;SIGHUP等待用户输入的一段时间将导致返回NULL,这将被视为EOFbashyy_readline_get()函数),然后才有机会运行延迟陷阱处理程序。

答案2

当 Bash 用完要读取的输入时也会退出。这可以通过多种方式发生,常见的方式是读取 shell 脚本的最后一行、用户键入 control-D,或者...关闭终端窗口。

(您也可以尝试bash -i < /dev/null并注意它如何立即退出,因为它耗尽了输入)。

答案3

除了叹息之外,幕后还发生了很多事情。例如,关闭终端窗口也会导致 pty 关闭,因此任何输出或输入都会返回 I/O 错误。

我们可以通过关闭窗口时运行进程strace来看到这一点。bash

我们从bash在提示符处等待的进程开始(pselect()),然后关闭窗口......

% strace -p 1090
strace: Process 1090 attached
pselect6(1, [0], NULL, NULL, NULL, {[], 8}) = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=3409, si_uid=500} ---
--- SIGCONT {si_signo=SIGCONT, si_code=SI_KERNEL} ---
rt_sigreturn({mask=[]})                 = -1 EINTR (Interrupted system call)
ioctl(2, TCXONC, TCOON)                 = -1 EIO (Input/output error)
ioctl(0, TCGETS, 0x7ffe1d1734e0)        = -1 EIO (Input/output error)
ioctl(0, SNDCTL_TMR_STOP or TCSETSW, {B38400 opost isig icanon echo ...}) = -1 EIO (Input/output error)

bash当我们尝试处理处理程序时,我们开始看到 I/O 错误...

此时请注意,bash 决定关闭,因为它没有控制终端,因此它恢复所有信号处理程序并向自己发送另一个 SIGHUP

rt_sigaction(SIGINT, {sa_handler=0x467410, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fe5c31b3060}, {sa_handler=0x4bb540, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fe5c31b3060}, 8) = 0
rt_sigaction(SIGTERM, {sa_handler=0x466f10, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fe5c31b3060}, {sa_handler=0x4bb540, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fe5c31b3060}, 8) = 0
rt_sigaction(SIGHUP, {sa_handler=0x4640e0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fe5c31b3060}, {sa_handler=0x4bb540, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fe5c31b3060}, 8) = 0
rt_sigaction(SIGALRM, {sa_handler=0x4676d0, sa_mask=[HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], sa_flags=SA_RESTORER, sa_restorer=0x7fe5c31b3060}, {sa_handler=0x4bb540, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fe5c31b3060}, 8) = 0
rt_sigaction(SIGWINCH, {sa_handler=0x466f00, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fe5c31b3060}, {sa_handler=0x4baaa0, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fe5c31b3060}, 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
getpid()                                = 1090
kill(1090, SIGHUP)                      = 0
--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=1090, si_uid=500} ---

然后关闭过程继续(重写.bash_history等)。

因此,终止 shell 的不是最初的 SIGHUP,而是失去提供输入终端的 pty。

相关内容