シグナルの概念

このエントリーをはてなブックマークに追加

ここではシグナルの概念について解説していきます。
そもそもシグナルとは、非同期割り込みを行う仕組みでハードウェア割り込みを拡張したものです。
UnixやPOSIXにはシグナルをどのように使用するか、どのような種類があるかが決められています。
これらは事前に発生するタイミングを知ることはできません。よって、「非同期」になんらかのイベントが発生しました、ということをプログラム側に伝える必要があります。これを実現する機能がシグナルなのです。


割り込み

ハードウェア割り込み

割り込みはハードウェア割り込みとソフトウェア割り込みの2つがあります。
例えば、ユーザがCtrl-Cでプログラムを中断しようとした場合、プログラムにバグがあり不正なメモリにアクセスしようとした場合等です。
割り込みは非常に便利な仕組みですが、いまいち使い方が分からないという方が多いのではないのでしょうか。ここでは割り込みの本来の使われ方を説明します。

もう少し詳しく言えば、ハードウェア割り込みとはCPUそのものが持っている機能です。
たとえば、ネットワーク通信を想像してください。PC:1とPC:2が通信を行なっているときに、PCAが送信したデータをPCBはいつ受け取る事をしれば良いのでしょうか?

単純な解決方法はデータが到着しているかを定期的に見に行く方法です。これをポーリングと言います。
この方法はそこそこ動きますが、非常に無駄が多い方法です。

そこで、ハードウェアは割り込みという機能を実装しています。
これは何らかのイベントが発生した事を知らせる機能で、ハードウェア割り込みが発生するとCPUはコンテキストを切り替え、割り込み処理が走ります。割り込み処理が終了すると、割り込まれた側は処理を再開します。これがハードウェア割り込みです。
これが便利なのは、割り込まれたことに気づかない!ということです。これにより割り込まれた側は特別な処理を入れなくても処理を続けられるのです。
これこそが、割り込みの便利な点です。これは非常に便利な為、大幅に拡張してソフトウェア上で実現したものがソフトウェア割り込みです。シグナルはソフトウェア割り込みの実装の1つです。


ソフトウェア割り込み

ハードウェア割り込みがハードウェアに由来し、通常はOSが処理するのに対して、ソフトウェア割り込みはソフトウェアが原因で発生します。
例えば、アクセスしてはダメなメモリにアクセスしたり、0除算などOS側で命令を実行中に判断出来る場合に発生します。
OSによって実装は様々ですが、これらを総称してソフトウェア割り込みと言います。


ハードウェア/ソフトウェア割り込みの抽象化

これらの異常なパターンもユーザ側から見ると、ハードウェア割り込みと基本的に変わりません。
そしてUnixを作った先人達はこれら雑多な割り込みを抽象化しました。これがシグナルです。

さらに、これらだけでなく、タイマー、ユーザ定義等の種類の追加、さらにシグナル毎によるシグナル発生時のデフォルトの動作(無視、終了等)の追加、動作の変更等、便利な抽象化が行なわれています。


シグナルの種類

シグナルはint型の整数で定義されています。有名なシグナルをいくつか紹介しましょう。

シグナルシグナル番号動作説明
SIGKILL9プログラムの強制終了
キャッチ、ブロック、無視はできません
一番有名で一番お世話になっているはず。
Ctrl+cで発生させられます
SIGSTOP9プログラムの一時停止
キャッチ、ブロック、無視はできません
これもよく使用しているはず。
Ctrl+zで発生させられます
SIGINT2キーボードからの割り込み動作が分かり辛いですがターミナルでCtrl-Cの時に発生するシグナルです
SIGINT2キーボードからの割り込み動作が分かり辛いですがターミナルでCtrl-Cの時に発生するシグナルです
SIGSEGV11不正なメモリ参照全プログラマの敵ですね。みんなのトラウマ!
原因はメモリ系のバグです。ただちにバグ修正する必要があります。
SIGBUS7,10バスエラー (不正なメモリアクセス)こちらも原因はメモリ系のバグです。ただちにバグ修正する必要があります。
SIGPIPE13パイプ破壊SIGSEGV、SIGBUSと同じくみんなのトラウマ!
こちらは例えばcloseしたパイプにwriteした場合などに発生します。

シグナルは扱いづらいですが、こうやってみるとバグの場合に強制終了したりするなど非常に有用な事が分かります。
また、シグナルはユーザ側で動作を変更する事ができます(出来ないシグナルもありますが)。正しく使えば非常に便利です。 signal(7)のmanに詳しく書いてあるので是非見てみてください。


シグナルを使ったコマンド

kill(1)はシグナルをプロセスに送信するだけのシンプルなコマンドですが、非常に便利な事はここの読者は実感しているでしょう。
さらに、派生のpkill(1)もあります。

kill(1)の使い方はたとえば、プロセスを強制終了させたければこのようにします。'SIGKILL'と言ったシグナルから'SIG'を削除した部分を指定すればよいのです。
pidはプロセスIDですがわからなければ、ps(1)で対象プロセスを調べればよいでしょう。
プロセス名でkillしたい場合はpkill(1)が使用できます。ただし、同じ名前のプログラムが複数起動している場合はすべてにシグナルが送られますので注意が必要です。

$ kill -kill [pid]
$ pkill -kill [process name]

また、シグナル番号でも指定できます。
$ kill -9 [pid]

シグナルと、シグナル番号の対応がわからなければ、killコマンドで表示できます。
$ kill -l
1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

気をつけて欲しいのは、このシグナル番号はシステムによって異なる場合もありますし、将来にわたって必ずしも同じとは限りませんので注意してください。
できるだけシグナル番号は使用しないほうがよいでしょう。


シグナルの動作を利用したプログラム

最後にシグナルを使った面白いジョークコマンドを1つ紹介しましょう。Unixだと大体動きますので一度インストールしてみてください。
プログラム名はsl(1)です。Linuxならばyumやaptなどでインストールできます。最近のMacであればPortやHomeBrewでインストール出来るでしょう。
lsと間違えてslを実行すればSL(機関車)AAが表示されます。うっとしくなるのですがCtrl-Cでも止める事はできません。'kill -kill'をしたくてもターミナルにはAAが表示されていてなにもできません。

      ====        ________                ___________
  _D _|  |_______/        \__I_I_____===__|_________|
   |(_)---  |   H\________/ |   |        =|___ ___|      _________________ 
   /     |  |   H  |  |     |   |         ||_| |_||     _|                \_____A
  |      |  |   H  |__--------------------| [___] |   =|                        |
  | ________|___H__/__|_____/[][]~\_______|       |   -|                        |
  |/ |   |-----------I_____I [][] []  D   |=======|____|________________________|_
__/ =| o |=-~~\  /~~\  /~~\  /~~\ ____Y___________|__|__________________________|_
 |/-=|___||    ||    ||    ||    |_____/~\___/          |_D__D__D_|  |_D__D__D_|
  \_/      \__/  \__/  \__/  \__/      \_/               \_/   \_/    \_/   \_/
これはシグナルの動作を変えるという機能を利用(悪用)した面白い例です。
さらに改悪をして長時間SLを表示し続ける物も公開されています。一度遊んで見てはいかがでしょうか?


まとめ

プログラマならば良く目にするシグナルですがあまり良い印象はないかもしれませんが、きちんと付きあえば便利な機能です。
動作に問題があれば教えてくれたり、動作をコントロールする事もできます。
次回はシグナル周りのシステムコールの使い方を紹介していきます。