shell と SIGHUP #2
id:lurkerさんのところのコメント欄の SIGHUP に関する話が面白い。そうか SIGHUP は端末ドライバが送信するものなのか。
ここからはボクの想像なのですが、使うシェルによって、
SIGHUPの送られ方が違うのはそのシェル達が
そういう実装をされているからだ思います。
たとえば、zshはSIGHUP受け取ったらジョブテーブルに載ってるプロセスすべてに
SIGHUPを送るという実装をされているのではないかと考えています。
そうするとdisownの説明がシンプルに行えるし、disownのmanの記述とも一致します。
ジョブテーブルから指定したプロセスを削除する、という。
まぁ、男は黙ってソースを読めばいいんじゃないかな。ということで zsh のソースを軽く探検。
まず、zsh の signal handler であるところの zhandler(Src/signals.c) を見てみるとこんな記述がある。
case SIGHUP: if (!handletrap(SIGHUP)) { stopmsg = 1; zexit(SIGHUP, 1); } break;
handletrap は trap で設定した handler があったりすると 1 を返すとかそういう関数(多分)なので置いておくとして、続いて zexit(Src/builtin.c) を見る。
mod_export void zexit(int val, int from_where) { /* snip */ if (isset(MONITOR)) { /* send SIGHUP to any jobs left running */ killrunjobs(from_where == 1); } /* snip */ }
再度 Src/signals.c に戻って killrunjobs を確認。
void killrunjobs(int from_signal) { int i, killed = 0; if (unset(HUP)) return; for (i = 1; i <= maxjob; i++) if ((from_signal || i != thisjob) && (jobtab[i].stat & STAT_LOCKED) && !(jobtab[i].stat & STAT_NOPRINT) && !(jobtab[i].stat & STAT_STOPPED)) { if (jobtab[i].gleader != getpid() && killpg(jobtab[i].gleader, SIGHUP) != -1) killed++; } if (killed) zwarn("warning: %d jobs SIGHUPed", killed); }
うん? unset(HUP) なら何もしないのか。
ちょっと試してみる。
% cat checkhup.pl #!/usr/bin/env perl use strict; use warnings; $SIG{HUP} = sub { print "recieved SIGHUP\n"; exit 1; }; while (1) { sleep 10; } % zsh % setopt nohup % setopt nocheckjobs % ./checkhup.pl & % exit % pgrep -lf checkhup.pl 11240 perl ./checkhup.pl
ふむ。SIGHUP は送信されないな。
とりあえず、現状での結論。zsh の場合でもシステムが直接 background process に SIGHUP を送るわけではない。
で zsh では setopt nohup していない場合 (or 明示的に setopt hup した場合は) はプロセスの終了時にジョブ管理下にあるプロセスには SIGHUP が送られると。tcsh っぽい挙動がいい人は setopt nohup; setopt nocheckjobs すればいいと思う。(ということをさっき jijixi さんのところにコメントしてみた)
まあ実は man zshoptions をよく読めば一発だったとかいうオチなんだけどね。