UNIX系OSにおけるシグナルについて
シグナルとは、
UNIX系
オペレーティングシステム(OS)における、
プロセス間通信の基本的な仕組みの一つです。これは、
プロセスに対して非同期でイベントの発生を通知する機構であり、OSはシグナルを受信した
プロセスの実行を中断し、特定の処理を実行させることができます。
シグナルの基本
シグナルは、
プロセスが実行中に発生したイベント(キーボード入力、エラー、システムからの通知など)を非同期的に伝えるために使われます。
プロセスは、シグナルを受信すると、事前に設定されたシグナルハンドラを実行するか、デフォルトの処理を行います。シグナルは、プログラムの正常な流れを中断できるため、プログラミングには細心の注意が必要です。
シグナルの割り込み
シグナルは、
プロセスの実行中のあらゆる段階で割り込むことができます。ただし、I/O待ちなどで
カーネル内部で処理がブロックされている場合は、シグナルは無効になります。シグナルハンドラが登録されている場合、シグナル受信時にそのハンドラが実行され、そうでない場合はデフォルトのシグナル処理が行われます。
シグナルの信頼性
POSIX標準に準拠したシグナルは、シグナルハンドラの実行開始時と終了時に、シグナルマスクの変更やスタックの切り替えをアトミックに行うことを保証しています。これにより、シグナルハンドラが確実に実行され、再入を防ぐことができます。
POSIX以前の実装では、これらの動作が欠落していたり、アトミックに実行されないため、予期せぬ問題が発生することがありました。
シグナルの送信
シグナルは、様々な方法で送信できます。
- - ユーザー操作: 端末で特定のキー(Ctrl+C、Ctrl+Zなど)を押すことで、シグナルが送信されます。
- - システムコール: `kill()`システムコールを使用すると、指定したプロセスに任意のシグナルを送信できます。
- - 例外: ゼロ除算やセグメンテーション違反などの例外が発生すると、対応するシグナルが送信されます。
- - カーネル: カーネルは、パイプがクローズされた場合など、特定のイベントが発生したときにシグナルを送信します。
- - `abort()`: プログラム内で`abort()`関数が呼ばれると、SIGABRTシグナルが送信されます。
シグナルの処理
シグナルを処理するには、`signal()`や`sigaction()`
システムコールを使用して、シグナルハンドラを設定します。シグナルハンドラが設定されていない場合は、デフォルトの動作(シグナルの無視、プログラム終了、
コアダンプなど)が実行されます。`SIGKILL`と`SIGSTOP`は、捕捉や処理ができない特別なシグナルです。
シグナルハンドラの注意点
シグナルハンドラは、通常の処理に割り込んで呼び出されるため、以下の点に注意する必要があります。
- - 再入可能性: シグナルハンドラは再入可能な処理のみを行うべきです。`malloc()`や`printf()`などの非同期シグナル安全でない関数を使用すると問題が発生する可能性があります。
- - シグナルマスク: シグナルハンドラ内で非再入可能な処理を行う場合は、処理中にシグナルをマスクし、完了後にマスクを解除する必要があります。
- - グローバル変数: `errno`などのグローバル変数の変更は、割り込まれた処理に予期しない影響を与える可能性があります。
シグナルは、実行中の
システムコールを中断することがあります。この場合、
システムコールは`EINTR`エラーを返します。アプリケーションは、このエラーを処理し、
システムコールを再実行する必要があります。4.2BSDでは、
システムコールが中断された場合、
カーネルが内部的に
システムコールを再開する機能が導入されました。
シグナルの問題点
シグナル処理は、競合状態に弱く、シグナルハンドラ実行中に別のシグナルが送られてくる可能性があります。この問題を解決するために、`sigprocmask()`を使ってシグナルの配送をブロックできます。また、シグナルハンドラでの処理は、可能な限り安全な方法で行う必要があります。
ハードウェア例外とシグナル
ハードウェア例外(
ゼロ除算、
セグメンテーション違反など)が発生した場合、
カーネルは例外を処理できない場合、対応するシグナルを
プロセスに送信します。例えば、
ゼロ除算は`SIGFPE`、不正なメモリアクセスは`SIGSEGV`に対応するシグナルが送信されます。
個々のシグナル
Single UNIX Specificationでは、多くのシグナルが定義されており、それぞれデフォルトの動作が定められています。主な動作には、
プロセスの終了、
コアダンプ、シグナルの無視、
プロセスの停止などがあります。
シグナルの歴史
シグナルの概念は、
UNIXの初期から存在しており、当初は
端末からの簡単な
プロセス制御を目的としていました。初期のシグナルは信頼性が低く、ハンドラの再入問題を抱えていましたが、SVR3や4.2BSDでシグナルマスクやハンドラの専用スタックが導入され、信頼性が向上しました。その後、
POSIXで標準化され、マルチスレッドにおけるシグナル仕様の拡張が行われました。
まとめ
シグナルは、
UNIX系OSにおいて、非同期イベントの通知や
プロセス制御に不可欠なメカニズムです。プログラマは、シグナルの動作や注意点を理解し、安全にシグナルを扱う必要があります。特に、シグナルハンドラでの処理は、再入可能性やシグナルマスクに配慮し、
システムコールの中断などの予期せぬ事態にも対処できるよう、適切なプログラミングを心がけましょう。