3.5. シグナル

シグナルは、Unix ライクな OS 世界における単純な「割り込み」方式で、 Unix では古くからある機能の 1 つです。 プロセスは、「シグナル」を別のプロセスに送れ(たとえば kill(1)や kill(2) を使って)、その相手のプロセスはそれを受取り、非同期にそのシグナルを 扱います。 プロセスが他のプロセスに任意のシグナルを送るには、送り手のプロセスが root もしくは、相手のプロセスの実もしくは実効ユーザ id の権限を持って いなければいけません。 しかし、シグナルを別の方法で送ることもできます。 特に SIGURG はネットワーク越しに TCP/IP のアウト・オブ・バンド(out-of-band (OOB))メッセージを使って送れます。 【訳註:「アウト・オブ・バンド」とは、データとは独立の通信経路を使って、 制御用情報を交換することです】

シグナルはいにしえからの Unix の機能の 1 つですが、実装も書き方もさまざまです。 「シグナルを処理している時に、他のシグナルが発生したらどうなるか?」という 根本的な問題を抱えています。 libc5 を採用している古い Linux ではシグナルのいくつかで操作方法が最新の GNU libc ライブラリと異なる点があります。 シグナルハンドラで C ライブラリ関数を安全に呼び出せない場合がよくあり、 システムコールの中にさえ、安全でないものがあります。 ドキュメントを確認して、シグナルから呼び出しても安全であることが保証されて いるかを確認してください。 詳しい情報は、glibc FAQ(ローカルに /usr/doc/glibc-*/FAQ というコピーがあるシステムもあります)を見てください。

新しくプログラムを書くなら、POSIX シグナルシステム(BSD のものにかわって) を使ってください。この仕組みは広くサポートされていて、古いシグナルシステム が持っていた問題のいくつかを解決しています。 POSIX シグナルシステムは、sigset_t というデータ型を使うことを前提に していて、そのデータ型を扱う関数によって操作できます。その関数は、 sigemptyset()と sigfillset()、sigaddset()、sigdelset()、sigismember()です。 sigsetops(3)にこれらの関数についての説明があります。 設定したなら、sigaction(2)や sigprocmask(2)、sigpending(2)、 sigsuspend(2)を使って、シグナルの操作を設定してください(詳細な情報は man を 見てください)。

通常はシグナルハンドラをどれもできるだけ短くかつ単純にし、競合状態に注意を 向けてください。 シグナルはそもそも非同期に発生するので、恐らく競合状態が起こるでしょう。

サーバにはある共通した慣例があります。SIGHUP を受けた場合には、ログファイル をすべて閉じ、設定ファイルを再オープンして読み込み、再びログファイルを開き ます。 これでサーバを止めず再設定が行え、データをなくすことなくログをローテーション できます。 何らかのサーバを書いていて、この慣例をなるほどと思うなら、ぜひこの機能を サポートしてください。

Michal Zalewski [2001] はどうやってシグナルハンドラが攻撃を受けるか について、素晴らしいチュートリアルを書きました。その中でシグナルの競合状態 をいかに排除するかについて、アドバイスをしています。 さらに情報を得たいなら要約を読むようにおすすめします。ここに書くものは、私が 推奨することですが、Michal 氏のものと同様です。