S-JIS[2006-01-06/2006-01-12]
UNIXはプロセスに対してシグナルを送って割り込み処理をさせることが出来る。
プログラム側はシグナルをマスクすることによって、シグナルを捕捉して独自の処理を行うことが出来る。
シグナルの種類はUNIXの種類によって異なるので、自分で使うOSに合わせる必要がある。(killコマンドを使えば、そのOSがどのシグナルをサポートしているのか確認できる)
sigtest.c:
#include <stdio.h> #include <signal.h> #include <pthread.h> void* SignalThread(void* arg) { sigset_t set; int r; //setを初期化 r = sigfillset(&set); if (r != 0) return NULL; //デフォルト動作を行うシグナルを指定(マスク対象から外す) r = sigdelset(&set, SIGKILL); if (r != 0) return NULL; r = sigdelset(&set, SIGSTOP); if (r != 0) return NULL; //シグナルのマスク r = pthread_sigmask(SIG_SETMASK, &set, NULL); if (r != 0) return NULL; while(1) { siginfo_t info; //シグナル捕捉 r = sigwaitinfo(&set, &info); if (r < 0) return NULL; switch(info.si_signo) { case SIGTERM: printf("シグナルによって終了します\n"); return NULL; default: printf("シグナル捕捉:%d\n", info.si_signo); break; } } } int main(int argc, char* argv[]) { sigset_t set; pthread_t th; int r; /* 全シグナルをブロック */ //setを初期化 r = sigfillset(&set); if (r != 0) return r; //シグナルのマスク r = pthread_sigmask(SIG_SETMASK, &set, NULL); if (r != 0) return r; /* シグナル処理専用スレッド作成 */ r = pthread_create(&th, NULL, SignalThread, NULL); if (r != 0) return -1; //スレッドの終了待ち r = pthread_join(&th, NULL); return r; }
コンパイル> cc sigtest.c -lrt -lpthread
まずメインスレッドで全シグナルをブロックし、シグナル処理専用スレッドを作って捕捉するシグナルだけマスクし直す。
シグナル処理スレッドで行っていることを スレッドにせずメインで直接行うと、なぜかシグナルを捕捉できない…。
sigfillset()によって 全シグナルを対象とするようにsetを初期化し、
sigdelset()によって デフォルト処理を行うシグナルをsetから除外していく。
そしてpthread_sigmask()を呼ぶ事によってマスクを実行する。
sigwaitinfo()を呼ぶと、シグナルが発生するまで待つ。
一定時間毎に戻ってくるようにしたいならsigtimedwait()を使用する。
info(siginfo_t構造体)の内容はsigaction()のマニュアルに説明がある。
シグナルの説明を見ると、SIGKILLとSIGSTOPは「捕捉することが出来ない」とされている。
では他のシグナルは全て捕捉できるかというと、必ずしもそうではないっぽい。
直接実行(&付) | > a.out & | |
シェル(&付)実行 | > a.sh & | シェルの中は「a.out」 |
シェル実行(シェル内で&付) | > a.sh | シェルの中は「a.out &」 |
こうして実行したプロセスに対しpsでa.outのプロセスIDを確認してkillでシグナルを送ったところ、捕捉出来ずにデフォルト動作をするものや、無視されるものがあった。
(SIGKILLとSIGSTOPを捕捉できるかどうかは試してない)
番号 | 名称 | デフォルト動作 | 内容 | 直接実行 (&付) |
シェル(&付) 実行 |
シェル実行 (シェル内で&付) |
---|---|---|---|---|---|---|
1 | SIGHUP | 終了 | ハングアップ(RS-232Cでの回線切断等) | 捕捉 | 捕捉 | 捕捉 |
2 | SIGINT | 終了 | 割り込み(rubout)(Ctrl+C等) | 捕捉 | 捕捉 | 無視 |
3 | SIGQUIT | コア | 終了(ASCII FS) | 捕捉 | 捕捉 | 無視 |
4 | SIGILL | コア | 不当な命令(捕捉されてもリセットされない) | 捕捉 | 捕捉 | 捕捉 |
5 | SIGTRAP | コア | トレーストラップ(捕捉されてもリセットされない) | 捕捉 | 捕捉 | 捕捉 |
6 | SIGABRT | コア | 異常終了時に使用(アボート) | 捕捉 | 捕捉 | 捕捉 |
7 | SIGEMT | コア | EMT命令(エミュレーション・トラップ) | 捕捉 | 捕捉 | 捕捉 |
8 | SIGFPE | コア | 浮動小数点の例外 | 捕捉 | 捕捉 | 捕捉 |
9 | SIGKILL | 終了 | 強制終了(捕捉や無視は不可能) | 終了 | 終了 | 終了 |
10 | SIGBUS | コア | バス・エラー(アドレス・エラー) | 捕捉 | 捕捉 | 捕捉 |
11 | SIGSEGV | コア | セグメンテーション違反(不正アドレス参照) | 捕捉 | 捕捉 | 捕捉 |
12 | SIGSYS | コア | システムコールへの引数誤り(不正システム・コール) | 捕捉 | 捕捉 | 捕捉 |
13 | SIGPIPE | 終了 | 読み手のないパイプ上への書き込み | 捕捉 | 捕捉 | 捕捉 |
14 | SIGALRM | 終了 | アラームクロック | 捕捉 | 捕捉 | 捕捉 |
15 | SIGTERM | 終了 | プロセス終了 | 捕捉 | 捕捉 | 捕捉 |
16 | SIGUSR1 | 終了 | ユーザー定義のシグナル1 | 捕捉 | 捕捉 | 捕捉 |
17 | SIGUSR2 | 終了 | ユーザー定義のシグナル2 | 捕捉 | 捕捉 | 捕捉 |
18 | SIGCHLD | 無視 | 子プロセス状態の変化 | 無視 | 無視 | 無視 |
19 | SIGPWR | 無視 | 電源障害による再起動 | 無視 | 無視 | 無視 |
20 | SIGWINCH | 無視 | ウィンドウサイズの変更 | 無視 | 無視 | 無視 |
21 | SIGURG | 無視 | ソケットの緊急状態 | 無視 | 無視 | 無視 |
22 | SIGPOLL | 終了 | ポーリング可能なイベント発生 | 捕捉 | 捕捉 | 捕捉 |
23 | SIGSTOP | 停止 | 停止(捕捉や無視は不可能) | 停止 | 停止 | 停止 |
24 | SIGTSTP | 停止 | ttyより要求されたユーザーストップ(Ctrl+Zなどのジョブ制御) | 停止 | 停止 | 無視 |
25 | SIGCONT | 無視 | 停止していたプロセスの再開 | 無視 | 無視 | 無視 |
26 | SIGTTIN | 停止 | バックグラウンドttyの読み込みを試みた | 停止 | 停止 | 無視 |
27 | SIGTTOU | 停止 | バックグラウンドttyの書き込みを試みた | 停止 | 停止 | 無視 |
28 | SIGVTALRM | 終了 | 仮想タイマーの時間切れ | 捕捉 | 捕捉 | 捕捉 |
29 | SIGPROF | 終了 | プロファイリングタイマーの時間切れ | 捕捉 | 捕捉 | 捕捉 |
30 | SIGXCPU | コア | CPUの限界をオーバー | 捕捉 | 捕捉 | 捕捉 |
31 | SIGXFSZ | コア | ファイルサイズの限界をオーバー | 捕捉 | 捕捉 | 捕捉 |
32 | SIGWAITING | 無視 | スレッド処理コードで使われていた予約シグナル | 無視 | 無視 | 無視 |
33 | SIGLWP | 無視 | スレッド処理コードで使われていた予約シグナル | 無視 | 無視 | 無視 |
34 | SIGFREEZE | 無視 | チェックポイント一時停止 | 無視 | 無視 | 無視 |
35 | SIGTHAW | 無視 | チェックポイント再開 | 無視 | 無視 | 無視 |
36 | SIGCANCEL | 無視 | スレッドライブラリで使われている取り消しシグナル | 無視 | 無視 | 無視 |
37 | SIGLOST | 終了 | リソースがない(レコードロックがない) | 捕捉 | 捕捉 | 捕捉 |
38 | SIGXRES | 無視 | リソース制御超過 | 無視 | 無視 | 無視 |
39 | SIGJVM1 | 無視 | Java Virtual Machine用に予約1 | 捕捉 | 捕捉 | 捕捉 |
40 | SIGJVM2 | 無視 | Java Virtual Machine用に予約2 | 捕捉 | 捕捉 | 捕捉 |
41 | 捕捉 | 捕捉 | 捕捉 | |||
42 | 捕捉 | 捕捉 | 捕捉 | |||
43 | 捕捉 | 捕捉 | 捕捉 | |||
44 | 捕捉 | 捕捉 | 捕捉 | |||
45 | 捕捉 | 捕捉 | 捕捉 | |||
46 | killコマンドでエラー(Bad signal number) |
SIGINT・SIGQUITに関しては、system()の説明に「SIGINTとSIGQUITは無視される」とあるので、もしかすると(シェルが内部でsystem()を使ってプログラムを実行しているとすれば)そのせいでシェルから実行した時は捕捉出来ないのかもしれない。
SIGTSTP〜SIGTTOUはCtrl+Zで止めてfgやbgで再開させる動作に関するものっぽいので、OSが処理してしまうから プロセスまでシグナルが来ないのかもしれない。
SIGWAITING〜SIGCANCELはスレッド関連の処理っぽいので、(プロセス内では最低1スレッドで動いているから)OSが処理してしまって プログラムまでシグナルが来ないのかもしれない。
SIGCHLDは子プロセス関連の処理っぽいのでOSが処理してしまうのかもしれないけれど、SIGPWR〜SIGURGについては捕捉できても良さそうなものだ…。捕捉してもたぶん無視するからどうでもいいけど(爆)
プロセスがシグナルをどう処理するよう指定されているかを確認するコマンドがある。[2006-01-12]
% psig プロセスID ←psコマンドで 確認したいプログラムのプロセスIDを取得して それを指定
プロセスID: a.out
HUP blocked,default
INT blocked,ignored
QUIT blocked,ignored
ILL blocked,default
TRAP blocked,default
ABRT blocked,default
EMT blocked,default
FPE blocked,default
KILL default
BUS blocked,default
SEGV blocked,default
SYS blocked,default
PIPE blocked,default
ALRM blocked,default
TERM blocked,default
USR1 blocked,default
USR2 blocked,default
CLD blocked,default NOCLDSTOP
PWR blocked,default
WINCH blocked,default
URG blocked,default
POLL blocked,default
STOP default
TSTP blocked,default
CONT blocked,default
TTIN blocked,default
TTOU blocked,default
VTALRM blocked,default
PROF blocked,default
XCPU blocked,default
XFSZ blocked,default
WAITING default
LWP default
FREEZE blocked,default
THAW blocked,default
CANCEL caught _sigcancel SIGINFO HUP,INT,QUIT,ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,CLD,PWR,WINCH,URG,POLL,TSTP,CONT,TTIN,TTOU,VTALRM,PROF,XCPU,XFSZ,WAITING,LWP,FREEZE,THAW,CANCEL,LOST,XRES,RTMIN,RTMIN+1,RTMIN+2,RTMIN+3,RTMAX-3,RTMAX-2,RTMAX-1,RTMAX
LOST blocked,default
XRES blocked,default
RTMIN blocked,default
RTMIN+1 blocked,default
RTMIN+2 blocked,default
RTMIN+3 blocked,default
RTMAX-3 blocked,default
RTMAX-2 blocked,default
RTMAX-1 blocked,default
RTMAX blocked,default
これで見ると、INT・QUITに関しては 捕捉してから無視される設定になってしまっている模様。
KILL・STOPはちゃんとデフォルト動作の設定になっている。
WAITING・LWPもなぜかデフォルト動作になっている。
でもFREEZE・THAWに関しては、捕捉する設定になってるな…。
trussコマンドを使って見てみると、sigtimedwait()
がシグナルを捕捉してはいるのだけれど、その内部で処理してしまうものがあるっぽい。
% kill -l HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM USR1 USR2 CLD PWR WINCH URG POLL STOP TSTP CONT TTIN TTOU VTALRM PROF XCPU XFSZ WAITING LWP FREEZE THAW CANCEL LOST RTMIN RTMIN+1 RTMIN+2 RTMIN+3 RTMAX-3 RTMAX-2 RTMAX-1 RTMAX