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

Tim*_*Tim 1 linux bash terminal-emulator signals sighup

在 lxterminal 的终端仿真器窗口中的 bash shell 中,我运行

$ trap "echo hello" SIGHUP 
$ kill -s HUP $$
hello
$
Run Code Online (Sandbox Code Playgroud)

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

关闭终端模拟器窗口是否只会导致 SIGHUP 发送到控制进程,即 bash 进程?

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

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

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

谢谢。

der*_*ert 5

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

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

  • @Tim,因为内核以不同的方式处理这些情况。如果您从终端/管道/字符设备/等读取。并且没有可用的输入,您的程序只是在“read”系统调用中等待,直到有输入,或者肯定不会再有输入。(有一堆系统调用处理对文件/设备的读/写,远远超出了注释中可以涵盖的范围,也可以在不等待的情况下检查状态)。通常这根本不涉及信号。 (2认同)

Ste*_*ris 5

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

当您关闭窗口时,我们可以通过stracebash进程上运行来看到这一点。

我们从bash在提示 (the 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)
Run Code Online (Sandbox Code Playgroud)

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} ---
Run Code Online (Sandbox Code Playgroud)

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

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


mos*_*svy 5

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

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

xterm -e bash --noediting
Run Code Online (Sandbox Code Playgroud)

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

trap 'echo HUP' HUP
Run Code Online (Sandbox Code Playgroud)

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

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

bash --rcfile <(echo 'trap "echo HUP" HUP')
Run Code Online (Sandbox Code Playgroud)

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

readline()被调用的函数bash会在等待用户输入时安装自己的信号处理程序,并在返回时恢复原始处理程序;一个SIGHUP而从用户等待输入将导致其返回NULL,这将作为被处理EOFbash(在yy_readline_get()具有运行推迟陷阱处理程序的机会之前功能)。