シグナル (ソフトウェア)

出典: フリー百科事典『ウィキペディア(Wikipedia)』
移動: 案内検索

シグナル: signal)とは、UNIXUnix系などのPOSIX準拠OSにおける限定的なプロセス間通信の一形態。プロセス間あるいはスレッド間で非同期イベントの発生を伝える機構である。シグナルが送信された際、OSは宛先プロセスの正常な処理の流れに割り込む。どんな不可分でない処理の間でも割り込むことができる。受信プロセスが以前にシグナルハンドラを登録しておけば、シグナル受信時にそのルーチンが実行される。さもなくば、デフォルトのシグナル処理が行われる。シグナルは1970年ごろベル研究所で開発されたUNIXを起源とし、後にPOSIXで標準化された。

シグナル送信[編集]

以下のような操作によりシグナルが送信される。

  • ユーザーがあるプロセスの端末のキーを押下したとき、端末がシグナルを発生する。
    • CTRL+C(古いUNIXでは DEL キー)を押下すると、SIGINT を送信し、デフォルトではそのプロセスを終了させる。
    • CTRL+Z を押下すると、SIGTSTP を送信し、デフォルトではプロセスの実行を中断(一時停止)させる。
    • CTRL+\ を押下すると、SIGQUIT を送信し、デフォルトではプロセスを終了させコアダンプさせる。
    • (なお、これらのキーの組み合わせは stty英語版 コマンドで変更可能である)
  • kill(2) システムコールを使うと、権限があれば指定したプロセス(群)に指定したシグナルを送信できる。同様に kill(1) コマンドでユーザーがプロセス(群)にシグナルを送信することもできる。また、raise(3) を使えばカレントプロセス(またはスレッド)に指定したシグナルを送信できる。
  • ゼロ除算(SIGFPE)、セグメンテーション違反(SIGSEGV)などの例外によってもシグナルが発生する。経験の浅いプログラマはポインタに不正アドレスを入れてしまい、SIGSEGVを発生させることが多い。これらはデフォルトではプログラムを終了させ、コアダンプを生じる。
  • カーネルはプロセスに何らかのイベントを通知するためにシグナルを発生させることができる。例えば、プロセスがパイプに書き込んだとき読み込み側プロセスが既にパイプをクローズしている場合にSIGPIPEが送信される。この場合、デフォルトではそのプロセスは終了となるが、パイプをシェルが構築した場合、終了させるのが最も便利である。

シグナル処理[編集]

signal()sigaction()英語版システムコールは「シグナルハンドラ」を設定するのに使われる。シグナルハンドラが設定されていないシグナルの場合、デフォルトのハンドラが使われる。さもなくば、シグナルは捉えられ、シグナルハンドラが呼び出される。プロセスはハンドラを設定しなくとも2種類のデフォルト動作を指定できる。シグナルを無視するか(SIG_IGN)、デフォルトのハンドラを使うか(SIG_DFL)である。SIGKILLとSIGSTOPは、捉えることもハンドラで処理することもできないシグナルである。

問題[編集]

ハンドラによるシグナル処理は競合状態に弱い。シグナルは非同期イベントなので、あるシグナルをハンドラで処理中に別のシグナル(同じ種類ということもある)がそのプロセスに送られてくることがある。このような状態を防ぐため、sigprocmask()英語版 を使ってシグナル配送のブロック/アンブロックが可能である。

シグナルは処理中のシステムコールを中断することがあり、アプリケーションは非透過的な再実行をしなければならない。つまり、実行中のシステムコールはEINTRというエラーを返し、要求した処理はシグナル受信によって中断されて結果を得られていないことを示す。そのため、処理を続行するには再度同じシステムコールを実行しなければならない。

シグナルハンドラは通常の処理に割り込んで呼び出されるので、不要な副作用を起こさないように注意が必要である。例えば、大域変数である errno を変化させてはならない。シグナルマスクを変化させてはならない。シグナル処理方法を変化させてはならない。その他のプロセスの大域的な属性を変化させてはならない。mallocprintfといったリエントラントでない関数を使うのも安全ではない。

ハードウェア例外との関係[編集]

プロセスの実行によってハードウェア例外が発生することがある。例えば、プロセスがゼロ除算を行おうとしたときや、TLBミスを引き起こしたときなどである。Unix系OSでは、ハードウェア例外が発生するとコンテキストを自動的に切り替えてカーネル例外ハンドラを実行開始する。ページフォールトなどの一部の例外の場合、カーネルはそのイベントを処理するのに十分な情報を持っているので、プロセスの実行を再開させることができる。しかし他の例外ではカーネルはうまく処理できず、代わりに例外を発生させた処理を行っていたプロセスに例外処理を委任しなければならない。シグナルはこの委任のための機構としても機能し、カーネルからプロセスに対してその例外に対応したシグナルを送信する。例えば、x86 CPU でゼロ除算を行おうとした場合 divide error 例外が発生し、カーネルがそのプロセスにSIGFPEというシグナルを送信する。同様に、あるプロセスが自身の仮想アドレス空間の範囲外のメモリアドレスにアクセスしようとした場合、カーネルはSIGSEGVシグナルを送信する。ハードウェア例外の種類はCPUのアーキテクチャによって異なるので、ある例外が発生したときどういうシグナルが送信されるかは厳密にはCPUアーキテクチャやカーネルの実装に依存する。

個々のシグナル[編集]

Single UNIX Specification では、以下のシグナルを <signal.h> で定義すべきものとして指定している。

DFは、シグナル受信時のデフォルト動作を示しており、以下のような処理がある[1]:

  • T: プロセスの異常終了。exit() システムコールを実行したのと同様の終了の仕方だが、wait()またはwaitpid()でそのプロセスの終了を待ち合わせていたプロセスには、シグナル受信で終了したことを示す異常終了コードが返される。
  • A: プロセスの異常終了。設定されていればコアダンプを生成する。
  • I: シグナルを無視する。
  • S: プロセスの実行を中断(一時停止)する。
  • C: 中断されていたプロセスの実行を再開する。中断されていないプロセスでは無視する。

下記表のシグナル番号は Linux x86 の場合であり[2]、他のOS・他のCPUでは異なる。Linux ARM もシグナル番号は同じ。

シグナル名 説明 DF 解説 Linux x86での
シグナル番号[2]
SIGABRT プロセスが中断された A abort()をプロセス自身が実行することによって発生する。ハンドラによるキャッチ可能。ブロック不可。シグナルハンドラから戻るとプロセスは終了させられる。何らかの不正な状況に陥ったが通常の処理の流れでは終了前のクリーンアップ処理ができないときに使用。 6
SIGALRM alarm() によるシグナル T alarm()システムコールで、設定した実時間タイマーがタイムアウトしたことを知らせる。 14
SIGBUS 「未定義メモリ領域へのアクセス」(SUS)によるバスエラー A 以下のような不正なメモリ操作により発生。ハンドラでキャッチできるが、ハンドラから戻った後の動作はシステムに依存する(通常プロセスの終了)。
  • アドレス境界不正:CPUはデータのサイズによって配置可能なアドレスに制限があることが多い。例えば32bit整数を4バイト境界(4で割り切れるアドレス)以外に置こうとしたなど。
  • 存在しない物理アドレス:ページフォールトに似ているが物理アドレスに関して発生。
  • オブジェクト固有エラー:例えば、mmapでマッピングされたファイルが切り捨てられたために仮想アドレスに対応する実体が存在しなくなった場合など。
7
SIGCHLD 子プロセスが終了、停止(または再開*)した I 子プロセスの状態変化に応じて発生。親プロセスは無視することもできる。 17
SIGCONT 停止していれば再開 C プロセスグループを参照。 18
SIGFPE 浮動小数点例外 -- 「不正な算術操作」(SUS) A 浮動小数点演算ゼロ除算やオーバーフローなどが発生したときに発生。Signaling NaNが発生したときも同様。また、整数のオーバーフローなどでも発生する。ハンドラでキャッチ可能だが、適切に処理しないとプロセスがハングアップ(無限ループ)に陥る可能性がある。(ここでいう適切な処理とは、シグナルハンドラが受け取る例外発生時のレジスタの内容を書き換えて例外が発生しないようにすることであり、例外発生箇所のコードを解析してどのレジスタが使われていたのかを調べたり、内容を書き換えることで計算結果が不正にならないか判断したりといった非常に高度な対応が必要とされる。また、Signaling NaN 以外では処理続行はほぼ不可能) 8
SIGHUP ハングアップ T 本来は端末の回線が切れたときに発生。現在では擬似端末をクローズしたときにその端末から起動されたプロセスグループに送られる。シェルの nohup 機能を使えばバックグラウンドのプロセスがSIGHUPを受け付けないようにできる。 1
SIGILL 不正命令 A 通常、命令でないメモリ領域にジャンプしたときに発生(コールスタックのリターンアドレスが破壊されたときなど)。他に特権レベルが高くないと実行できない命令を実行しようとしたときなどにも発生する。 4
SIGINT 割り込み T 端末から割り込みキー(通常 CTRL + C)を押下したときに発生。 2
SIGKILL 強制終了(kill) T killコマンドなどで明示的に発生させる。キャッチしたり無視したりできない。ゾンビプロセスは既に終了しているのでSIGKILLを受けても消滅しない。スリープ中プロセスはスリープ解除されたときにSIGKILLを受け付ける。initはSIGKILLを無視できる。 9
SIGPIPE 読み手のいないパイプへの書き込み T 13
SIGPOLL*, SIGIO poll可能イベント T ファイル記述子に対してfcntl()システムコールでシグナル発生を指示すると、ポール可能イベントに伴ってSIGPOLLが発生する。一般に通信ポートに対応するファイル識別子に使用し、完全な非同期I/Oを実現する。ただし、コードが複雑化することから、最近では専用の非同期I/Oシステムコールを使用することが推奨されている。 29
SIGPROF* プロファイリングタイマーがタイムアウト T プロファイラで使用。このときのタイマーはプロセスの全実行時間に対応する(CPUモードに関わらず計時する)。 27
SIGPWR 電源喪失 T 30
SIGQUIT 終了とコアダンプ A 通常、端末からの終了キー(CTRL + \)で発生。 3
SIGSEGV セグメンテーション違反 A ページフォールトのうち不正なメモリアクセスによるものの場合に発生。しかし、例えばヒープ領域の拡張をSIGSEGV発生を受けてオンデマンドで行うライブラリなどもある。 11
SIGSTKFLT 数値演算プロセッサにおけるスタックフォルト A 16
SIGSTOP 実行中断 S プロセスグループの実行中断のためのシグナル。キャッチも無視もできない。SIGCONT シグナルで実行再開する。 19
SIGSYS*, SIGUNUSED 不正 システムコール A 一般にシステムコールの番号や引数が不正だったときは単にエラーを返すことで済むので、このシグナルは滅多に使われない。 31
SIGTERM 強制終了 T killコマンドがデフォルトで発生するシグナル。しかし、このシグナルをキャッチしたり無視したりすることも可能。 15
SIGTRAP* トレース/ブレークポイントによるトラップ英語版 A 何らかのデバッグ機能で使用される(アーキテクチャによって異なる)。 5
SIGTSTP 端末からの中断シグナル S フォアグラウンドのプロセスグループを実行中断させる(通常、CTRL + Z 押下)。 20
SIGTTIN バックグラウンドプロセスが端末から読もうとした S バックグラウンドのプロセスグループがユーザー入力待ちとなって停止。シェルの機能を使ってフォアグラウンドにすることで入力が可能。 21
SIGTTOU バックグラウンドプロセスが端末に書き込もうとした S バックグラウンドのプロセスグループが端末への表示待ちとなって停止。シェルの機能を使ってフォアグラウンドにすることで表示可能。 22
SIGURG ソケット上に緊急データがある I 非同期I/O機能で使用。 23
SIGUSR1 ユーザー定義シグナル 1 T POSIXでは未定義。Linuxではスレッド間同期に使用。ddコマンドが受信すると操作の状況が表示される。 10
SIGUSR2 ユーザー定義シグナル 2 T POSIXでは未定義。Linuxではスレッド間同期に使用。 12
SIGVTALRM* 仮想時間をカウントするタイマによるシグナル -- 「仮想タイマのタイムアウト」(SUS) T プロファイラなどで使用。このときのタイマーはプロセスのユーザーモードでの実行時間を計時するもの。SIGPROFと組み合わせると、プロセスのカーネルモードでの実行時間がわかる。 26
SIGWINCH ウィンドウ リサイズ シグナル I 28
SIGXCPU* CPU時間制限を越えた A プロセス実行時間(スリープしていた時間やスケジューリング待ち時間は除く)がある値を超えると発生。 24
SIGXFSZ* ファイルサイズ制限を越えた A ファイルサイズがファイルシステム(あるいはオペレーティングシステム)の制限を越えようとしたとき、それを引き起こしたプロセスに送信される。 25

注:アスタリスク付の項目は、X/Open System Interfaces (XSI) による拡張を示す。(SUS)とある部分はSUS[1]にある表現の引用(を和訳したもの)。

上述以外に、プロセスは擬似シグナル(番号0)を送信することもできる。これは実際にはシグナルを送信せずにシグナル送信時のエラーチェックをし、例えば宛先プロセスが存在するかどうかをチェックするのに便利である。

脚注[編集]

  1. ^ a b signal.h”. IEEE Std 1003.1, 2004 Edition. 2012年7月10日閲覧。
  2. ^ a b signal(7) – JM Project Linux Overview, Conventions and Miscellanea マニュアル

関連項目[編集]

外部リンク[編集]