SIGSTOP 問題


そろそろ obsolete ではなかろーかという カーネルが suspend 直前に SIGSTOP を発行、resume 直後に SIGCONT を発行してる件。

bash がどーこーいうんは (bash 使わんので) 中立を守ったけど、 tcp connection の扱いのほうがポリシー的に気に入らんかったので、 けっきょく手持ちの A300 カーネルでは SIGSTOP の発行を止めてしまった (ので、シェルレベルでの対策はしてないし、する予定もない。 どーせ今月末には B, C のカーネルソースも出るんだし)。

通信するプログラムの多くで suspend 時の挙動が変わるはずだが、 通信中に suspend するよーな奴のことまで面倒みるひつよーはないと思う。

カーネルソース

A300 で言えば arch/arm/mach-cotulla/discovery_apm.csend_event() が発行してる。

job control

SIGSTOP は、それを受けるプロセスはトラップできないし、無視することもできない。 そのまま実行を一時停止する。 しかし、その一時停止したという事実のほうは親プロセスは知ることができる ( wait(WUNTRACED))。

シェルは自分の上で動いてるプロセスが SIGSTOP を受けて停まると、 それを backgroud に回して自分が表に出ようとする。 また、標準の状態では、^Z で表のプロセスに SIGSTOP を送ることができる。

これら、裏にまわったプロセスを再び表に出すのには fg を使うが、 このとき、シェルが使う端末の capability と停止されていたプログラムの使っていた capability が異なっていても、一般にシェルはかつての capability を復活させない。

emacs や ng の場合、^Z すると適当に前処理してから自分自身に SIGSTOP を送っている。 また fg で再開された時、自分が再開したプロセスであるという事実を知って 端末を再設定しているが、これが本来の姿らしい。
つまり、SIGSTOP はトラップできないが、SIGCONT のほうはトラップできる。 SIGCONT を受けて自分が再開した時、端末は自分が要求する姿でないことがあるので、 自前で再設定するようなハンドラを sigaction(SIGCONT) に設定しておかなければならない。

が、誰もそんなことしてねーよ ...。 emacs ですら、sigaction(SIGCONT) を設定するのは ^Z (M-x suspend-mode) した時だけだ。

bash の場合

$ set +m
しておくと bash は自分の上で動いてるプログラムが SIGSTOP を受けて停まっても、 そのことを感知しなくなる。 bash が表にでない (つまり bash 用に端末を再設定しない) ので、もちろん SIGCONT で再開しても 端末は正しい状態のままになっている。

しかし子プロセスが停まってもそれを感知しないということは、 emacs などを ^Z で止めてもシェルが表にでてこないということでもある。 ^S で止めたのと同然の姿。

まあ、konsole は multi screen だからあんまり困らないか? でもこれって job control の意味ないって。

zsh の場合

ちょいと zsh の歴史をひもとくと、
zsh 2.x → zsh 3.0 → 3.0.1 → 3.0.2 → 3.0.3 → 3.0.4 → 3.0.5 〜 3.0.8 ...
                                ||
                               3.1.0 → 3.1.1 →  ... → 3.1.9 → 4.0.1 → ...
3.0.4 から fg 復帰のときに端末の設定を回復するようになった。このため、 3.0.4 以降の zsh では SIGSTOP で停まったプロセスが正しく復帰できる。 しかし、3.0.2 の系譜につらなる 4.0.x は、このパッチが当たってない。 このため、4.0.x では bash と同じく SIGSTOP で停まったプロセスの再開は保証されない。

4.0.x で 3.0.8 と同じことをさせるのは Src/jobs.c に↓を当てればいい:

さりげなくバイナリも置いてあるけど、 まだ zaurus では実行テストしてないし、パッケージにもしてない。 zsh3 では嫌という人がパッケージをインストールして使うだけという状況が想像できん。 どーせモジュールの取捨選択があるだろう。

それ以外のシェルの場合

ash (0.3.8), tcsh (6.11.00) が bash と同じ挙動、 ksh (pdksh 5.2.14) が zsh (3.0.x) と同じ挙動になっている。
zsh と違って他のシェルでは version 違いでの挙動変化は見てない :-)

tcp セッション

SIGSTOP の影響をうけるのは端末だけじゃない。

ssh で session を張っている時に A300 が suspend してしまうと ssh は停まる(当然だ)。 ここで A300 を resume しても、session は回復しない。落ちている。

断っておくが、後者は「当然のこと」じゃないぞ。 ふつーのノートパソコンに外から入った状態で suspend/resume すればちゃんと tcp session は復帰する (udp のほうはというと切断という概念がないから、そもそも切れよーがない)。 で、こいつを叩き落しているのも SIGSTOP である。

suspend したときに通信回線を close させたかったんだろうが、 おおきなお世話である。

というわけで、カーネルが SIGSTOP 発行するのを止めさせた。 SIGCONT のほうはそのままなので、SIGCONT を受けて色々してるプロセスに影響はないし、 SIGSTOP を受けてごちゃごちゃやってるプロセスがあるはずはないから、これでも問題は起きないはずだ。

SIGSTOP が発行されなくなったので、bash のままでも konsole がおかしくなるといった問題は自動的になくなった。 もちろん、suspend/resume しても ssh or telnet session は切れない。ちゃんと復活する。


[日記へ] [目次へ]