yash 2: シグナル処理の実装方針
POSIX シェルにはトラップという機能がある。従来の yash では実装していなかったが、yash が POSIX に準拠するためにはこれも実装しなければならない。
トラップというのは、予め trap
コマンドでシグナルの種類とコマンドを設定しておくと、そのシグナルをシェルが受信したときに自動的にコマンドが実行されるという機能だ。もちろんシグナルハンドラの中で直接コマンドを起動するなどということはできないので、シグナルを受信したら何らかの方法でそれを覚えておいて、適切なタイミングで受信したシグナルの種類を判別して対応するコマンドを実行しなければならない。
bash は、シグナルを受信したらシグナルハンドラでフラグを立てる (受信回数を表す数値をインクリメントする) ということをしている。しかし静的記憶域期間を持つ変数の値をシグナルハンドラからインクリメントするのは C99 では未定義の動作であるとされているので、規格に対する厳密な準拠を目指す yash ではこれはできない。
dash も bash と似たようなことをしているが、値をインクリメントするのではなく単に 1 を代入しているだけである。この方式だと同じ種類のシグナルを複数回受信してもトラップが一回しか実行されないことがある。
zsh はキューに受信したシグナル番号を入れるということをしているようだ。キューが一杯になると溢れたシグナルは無視される。しかし zsh は場合によってはシグナルハンドラ内で直接コマンドを実行するということもしている。大丈夫なのか、これ?
bash も dash も、シグナル番号をそのまま配列の添字にしている。これはシグナル番号がそれなりに小さな数であるからできることであって、シグナル番号が例えば 1000000 のような大きな数になっている環境では問題がある。実際にそんな環境があるとは思えないけれども、C99 および POSIX はシグナル番号は正の整数とする
としか定めていないので、そのような環境がある可能性を考慮しない訣にはいかない。もっともこれは、シグナル番号をそれなりに小さな正整数に変換する非同期シグナルセーフかつ単射な関数を作れば解決できる。
これ以外の方法としては、シグナルをブロックするという方法も考えられる。トラップが設定されたシグナルは sigprocmask でブロックしておき、適当なタイミングで sigpending でどのシグナルが来たかを調べ、対応するコマンドを実行する、という方法。ブロックしたまま処理待ちのシグナルを削除するにはダミーのシグナルハンドラを設定してから待ち時間 0 で pselect するとよい。
しかし、この方法はコマンドの入力待ちとの相性が悪い。コマンドの入力を待つ際には pselect を使うが、シグナルをブロックしたまま pselect を中断させることが出来ないので、結局シグナルハンドラ内でフラグを立てなければいけなくなる。
結局シグナルハンドラでフラグを立てる方法に落ち着きそうだなぁ。
| 固定リンク
コメント