web-dev-qa-db-ja.com

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

Lxterminalのターミナルエミュレータウィンドウのbashシェルで、

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

次に、ターミナルエミュレータウィンドウを閉じます。

ターミナルエミュレータウィンドウを閉じると、SIGHUPが制御プロセス(bashプロセス)に送信されるだけですか?

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

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

ターミナルエミュレータが重要です。 xtermウィンドウで実行されているbashでは、トラップを""に設定すると、xtermウィンドウを閉じることができなくなりますが、トラップをecho helloに設定すると、xtermウィンドウを閉じることができます。

ありがとう。

1
Tim

[さまざまなターミナルエミュレータの実際の動作と考えられる動作は無視します。完全に合理的な動作は、_^D_(VEOF)をウィンドウクローズ/ _WM_DELETE_WINDOW_のptyに送信することです。これを破棄して、そこで実行されているプロセスに受信させるのではありません。 SIGHUP;以下は、xtermを想定しており、その場合はSIGHUPをシェルのプロセスグループに送信します]。

表示されている動作は、独自のシグナルハンドラーをインストールしているreadlineライブラリが原因です。次のことを試してみてください。

_xterm -e bash --noediting
_

(または_bash --noediting_の代わりにdashzsh、またはksh)、次に実行します

_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_(= readlineを使用しない)で開始しました

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

5
mosvy

Bashは、読み取る入力がなくなると終了します。これはいくつかの方法で発生する可能性があります。一般的な方法は、シェルスクリプトの最後の行を読み取る、ユーザーがcontrol-Dを入力する、または...ターミナルウィンドウを閉じることです。

bash -i < /dev/nullを試して、入力が不足したためにすぐに終了する方法を確認することもできます)。

5
derobert

単なるSIGHUPだけでなく、内部ではさらに多くのことが起こっています。たとえば、ターミナルウィンドウを閉じると、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の損失です。

5
Stephen Harris