システムインタフェース

ハンドリング

アプリケーションプログラムは、特定のシグナルが受信されると呼び出される関数「シグナルハンドラ」を指定できます。シグナルを受信してシグナルハンドラが呼び出されることを、シグナルを「キャッチする」と言います。プロセスは、次のいずれか 1 つの方法でシグナルを処理できます。

シグナルハンドラは、通常はプロセスの現スタック上で実行します。こうするとシグナルハンドラは、プロセスで実行が割り込まれた位置に戻ることができます。シグナルハンドラが特定のスタックに実行するように、これをシグナルごとに変更できます。割り込まれたコンテキスト以外のコンテキストで再開しなければならない場合は、プロセスは以前のコンテキスト自体を復元しなければなりません。

シグナルハンドラのインストール

signal(3C)sigset(3C)signal(3UCB)、および sigvec(3UCB) すべてを使用して、シグナルハンドラをインストールできます。これらのすべては、シグナルに対する前の動作を戻します。4 つのインタフェースには、1 つの重要な違いがあります。signal(3C) は System V シグナルセマンティクスとなります (同じシグナルがそのハンドラに割り込むことができます)。sigset(3C)signal(3UCB)、および sigvec(3UCB) は、すべて BSD シグナルセマンティクスになります (シグナルはそのハンドラが復帰するまでブロッキングされます)。さらに、signal(3C)signal(3UCB) の両方とも、シグナルを無視するかデフォルトの動作を復元するかを制御できます。例 4-1 は、簡単なハンドラとそのインストールを示しています。


例 4-1 シグナルハンドラのインストール


#include <stdio.h>
#include <signal.h>

void sigcatcher()
{
		printf ("PID %d caught signal.¥n". getpid());
}

main()
{
		pid_t ppid;

		signal (SIGINT, sigcatcher);
		if (fork() == 0) {
			sleep( 5 );
			ppid = getppid();
			while( TRUE )
				if (kill( ppid, SIGINT) == -1 )
					exit( 1 );
		}
		pause();
}

シグナル SIGKILL または SIGSTOP に対して、ハンドラのインストールまたは SIG_IGN の設定を試みるとエラーになります。シグナル SIGCONT に対して SIG_IGN の設定を試みてもエラーになります。これはデフォルトで無視されているためです。

シグナルハンドラをインストールすると、signal(3C)sigset(3C)signal(3UCB)、または sigvec(3UCB) をもう一度呼び出して明示的に置き換えるまで、インストールされたままになります。

SIGCHILD のキャッチ

子プロセスが停止または終了すると、SIGCHILD が親プロセスに送られます。このシグナルへのデフォルトの応答は無視することです。このシグナルをキャッチでき、すぐに wait(2) および wait3(3C) を呼び出して、子プロセスからの終了ステータスを得ることができます。こうすると、ゾンビプロセスのエントリをできるだけ素早く削除できます。例 4-2 は、SIGCHILD をキャッチするハンドラのインストールを示しています。


例 4-2 SIGCHILD をキャッチするハンドラのインストール


#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/resource.h>

void proc_exit()
{
		int wstat;
		union wait wstat;
		pid_t	pid;

		while (TRUE) {
			pid = wait3 (&wstat, WNOHANG, (struct rusage *)NULL );
			if (pid == 0)
				return;
			else if (pid == -1)
				return;
			else
				printf ("Return code: %d¥n", wstat.w_retcode);
		}
}
main ()
{
		signal (SIGCHLD, proc_exit);
		switch (fork()) {
			case -1:
				perror ("main: fork");
				exit (0);
			case 0:
				printf ("I'm alive (terporarily)¥n");
				exit (rand());
			default:
				pause();
		}
}

SIGCHILD をキャッチャするハンドラは、通常はプロセス初期化の一部として設定します。これらは、子プロセスをフォークする前に設定しなければなりません。典型的な SIGCHILD ハンドラは、子プロセスの終了状態を検索します。