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 をよく読めば一発だったとかいうオチなんだけどね。