S-JIS[2006-01-06/2006-01-12]

UNIXシグナル

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を捕捉できるかどうかは試してない)

Solaris SunOS5.9(SunStudio10)の場合
番号 名称 デフォルト動作 内容 直接実行
(&付)
シェル(&付)
実行
シェル実行
(シェル内で&付)
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)      

SIGINTSIGQUITに関しては、system()の説明に「SIGINTとSIGQUITは無視される」とあるので、もしかすると(シェルが内部でsystem()を使ってプログラムを実行しているとすれば)そのせいでシェルから実行した時は捕捉出来ないのかもしれない。

SIGTSTPSIGTTOUCtrl+Zで止めてfgbgで再開させる動作に関するものっぽいので、OSが処理してしまうから プロセスまでシグナルが来ないのかもしれない。

SIGWAITINGSIGCANCELはスレッド関連の処理っぽいので、(プロセス内では最低1スレッドで動いているから)OSが処理してしまって プログラムまでシグナルが来ないのかもしれない。

SIGCHLDは子プロセス関連の処理っぽいのでOSが処理してしまうのかもしれないけれど、SIGPWRSIGURGについては捕捉できても良さそうなものだ…。捕捉してもたぶん無視するからどうでもいいけど(爆)


シグナル処理の確認

プロセスがシグナルをどう処理するよう指定されているかを確認するコマンドがある。[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

これで見ると、INTQUITに関しては 捕捉してから無視される設定になってしまっている模様。

KILLSTOPはちゃんとデフォルト動作の設定になっている。

WAITINGLWPもなぜかデフォルト動作になっている。
でもFREEZETHAWに関しては、捕捉する設定になってるな…。

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

技術メモへ戻る
メールの送信先:ひしだま