ターミナルエミュレータウィンドウを閉じるとbashプロセスが終了し、SIGHUPトラップが終了しないように変更されるのはなぜですか?

ターミナルエミュレータウィンドウを閉じるとbashプロセスが終了し、SIGHUPトラップが終了しないように変更されるのはなぜですか?

lxterminalターミナルエミュレータウィンドウのbashシェルで、次のコマンドを実行します。

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

その後、端末エミュレータウィンドウを閉じます。

ターミナルエミュレータウィンドウを閉じると、SIGHUPは制御プロセス(bashプロセスなど)にのみ送信されますか?

SIGHUPトラップはbashプロセスを終了しないため、bashプロセスは終了しないと予想しましたが、実際にbashプロセスが終了したのはなぜですか?

トラップを(無視)に変更しても""同じことが起こります。

端末エミュレータが重要です。 xtermウィンドウで実行されているbashで""xtermウィンドウを閉じることができないようにトラップを設定し、同時にecho helloxtermウィンドウを閉じるようにトラップを設定します。

ありがとうございます。

答え1

[他の端末エミュレータの実際の可能な動作は無視されます。^Dウィンドウを閉じてptyで実行されているプロセスに次のものを受け取らせるのではなく、ptyに()を送信するのは完全に合理的な動作です。家庭VEOFWM_DELETE_WINDOWSIGHUPxterm、この場合はSIGHUPシェルのプロセスグループに送信します。

あなたが見ている動作は、readlineライブラリが独自のシグナルハンドラをインストールするために発生します。以下を試してみると:

xterm -e bash --noediting

(またはdashzshまたはksh代わりにbash --noediting)次に実行します。

trap 'echo HUP' HUP

端末でウィンドウを閉じることができなくなります。HUPウィンドウを強制的に閉じようとすると、シェルがxkill予想されますEIO

以下は、観測中の動作のより簡単なテストケースで、端末エミュレータは含まれていません。端末で次のコマンドを実行します。

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

その後、kill -HUP $$単に印刷されますHUP。ただし、(sleep 1; kill -HUP $$) &(またはkill -HUP <pid>別のウィンドウで)次に起動しないと、シェルは印刷され終了しますexit--noediting= readlineを使用しません)。

readline()呼び出された関数は、ユーザーbash入力を待っている間に独自のシグナルハンドラーをインストールし、ユーザー入力を待っている間に戻って元のSIGHUPハンドラーを復元します。これはreturnとしてNULL扱われますEOFbashyy_readline_get()関数)遅延トラップハンドラを実行する機会がある前に。

答え2

Bashは、読み込む入力が不足しても終了します。これはいくつかの方法で起こり得る。一般的な方法は、シェルスクリプトの最後の行を読むこと、ユーザーが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など)。

したがって、シェルをシャットダウンすることは、最初のSIGHUPではなく、入力端子を提供するptyの損失です。

関連情報