Lxterminalのターミナルエミュレータウィンドウのbashシェルで、
$ trap "echo hello" SIGHUP
$ kill -s HUP $$
hello
$
次に、ターミナルエミュレータウィンドウを閉じます。
ターミナルエミュレータウィンドウを閉じると、SIGHUPが制御プロセス(bashプロセス)に送信されるだけですか?
SIGHUPトラップはbashプロセスを終了しないので、bashプロセスは終了しないと思いますが、なぜbashプロセスが実際に終了するのですか?
トラップを""
(無視)に変更した場合も同じことが起こります。
ターミナルエミュレータが重要です。 xtermウィンドウで実行されているbashでは、トラップを""
に設定すると、xtermウィンドウを閉じることができなくなりますが、トラップをecho hello
に設定すると、xtermウィンドウを閉じることができます。
ありがとう。
[さまざまなターミナルエミュレータの実際の動作と考えられる動作は無視します。完全に合理的な動作は、_^D
_(VEOF
)をウィンドウクローズ/ _WM_DELETE_WINDOW
_のptyに送信することです。これを破棄して、そこで実行されているプロセスに受信させるのではありません。 SIGHUP
;以下は、xterm
を想定しており、その場合はSIGHUP
をシェルのプロセスグループに送信します]。
表示されている動作は、独自のシグナルハンドラーをインストールしているreadline
ライブラリが原因です。次のことを試してみてください。
_xterm -e bash --noediting
_
(または_bash --noediting
_の代わりにdash
、zsh
、または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)、遅延トラップハンドラーを実行する前。
Bashは、読み取る入力がなくなると終了します。これはいくつかの方法で発生する可能性があります。一般的な方法は、シェルスクリプトの最後の行を読み取る、ユーザーがcontrol-Dを入力する、または...ターミナルウィンドウを閉じることです。
(bash -i < /dev/null
を試して、入力が不足したためにすぐに終了する方法を確認することもできます)。
単なる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の損失です。