[Я буду игнорировать фактическое и возможное поведение различных эмуляторов терминала; вполне разумным поведением было бы отправить pty^D
(VEOF
)при закрытии окна / WM_DELETE_WINDOW
вместо того, чтобы разорвать его и заставить запущенный в нем процесс получить SIGHUP
; следующее предполагает xterm
, что в этом случае отправит SIGHUP
группе процессов оболочки].
Поведение, которое вы видите, связано с библиотекой readline
, которая устанавливает свои собственные обработчики сигналов. Если вы попробуете следующее:
xterm -e bash --noediting
(или dash
, zsh
или ksh
вместо bash --noediting
), затем запустите
trap 'echo HUP' HUP
в терминале окно станет -незакрытым; оболочка просто напечатает HUP
, как и ожидалось, при попытке закрыть окно; принудительно закрыть его (например. сxkill
)приведет к выходу оболочки с ошибками EIO
, что вполне ожидаемо, поскольку pty был разорван.
Вот более простой пример поведения, которое вы наблюдаете, без использования эмуляторов терминала. Запустите следующее в своем терминале:
bash --rcfile <(echo 'trap "echo HUP" HUP')
Затем kill -HUP $$
просто напечатает HUP
, но(sleep 1; kill -HUP $$) &
(или kill -HUP <pid>
из другого окна )заставит оболочку напечатать exit
и выйти, если только вы не запустили ее с помощью--noediting
(= don не использовать readline)
Функция readline()
, вызываемая bash
, установит свои собственные обработчики сигналов при ожидании ввода от пользователя и восстановит исходные обработчики при возвращении; a SIGHUP
во время ожидания ввода от пользователя вызовет возврат NULL
, который будет обработан как EOF
функциейbash
(в функцииyy_readline_get()
), прежде чем появится возможность запустить отложенный обработчик ловушек.
Bash также завершает работу, когда заканчивается ввод для чтения. Это может произойти несколькими способами, наиболее распространенными из которых являются чтение последней строки сценария оболочки, ввод пользователем control -D или... закрытие окна терминала.
(Вы также можете попробовать bash -i < /dev/null
и заметить, как он немедленно завершает работу, потому что у него закончились входные данные ).
Под обложкой происходит гораздо больше, чем просто SIGHUP. Например, закрытие окна терминала также приводит к закрытию pty, поэтому любой вывод или ввод будут возвращать ошибки ввода-вывода.
Мы можем увидеть это, запустив 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
пытается обработать обработчик...
В этот момент обратите внимание, что 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
и так далее ).
Таким образом, не первоначальный SIGHUP завершает работу оболочки, а потеря pty, предоставляющего терминал для ввода.