標準シグナルとリアルタイムシグナルについての検証1:標準シグナルの動作
(プログラムの概要)
標準シグナルとリアルタイムシグナルの比較を行ってみたいと思います。今回はまず標準シグナルの場合です。プログラムの動きとしてはシグナルハンドラーが起動された回数を1秒おきに表示させたいと思います。
(注)今回は実験の為にシグナルハンドラー内で無駄な処理を行い重たくしてあります。シグナルハンドラーはできるだけ軽く作るのが鉄則です。また、実際に動作させると1秒おきに表示がでないことが分かると思いますが、これについては次回以降解説します。

/* * sample program * signal handler (SIGUSR1) */ #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <string.h> #include <time.h> #include <unistd.h> #include <errno.h> int sigcnt; void SignalHandler(int); int _nanosleep(int, int); int main(void) { struct sigaction action; printf("sample program(%s) start\n", __FILE__); memset(&action, 0, sizeof(action)); /* set signal handler */ action.sa_handler = SignalHandler; action.sa_flags = SA_RESTART; sigemptyset(&action.sa_mask); if(sigaction(SIGUSR1, &action, NULL) < 0){ perror("sigaction error"); exit(1); } /* loop */ while(1){ _nanosleep(1, 0); /* sleep 1 sec */ printf("%d\n", sigcnt); } return 0; } void SignalHandler(int signum) { char mem1[1024]; int i = 0; sigcnt++; for(i = 0; i < 10000; i++){ memset(mem1, 0, sizeof(mem1)); } return; } int _nanosleep(int sec, int nsec) { struct timespec req, rem; req.tv_sec = sec; req.tv_nsec = nsec; rem.tv_sec = 0; rem.tv_nsec = 0; while(nanosleep(&req, &rem)){ if(errno == EINTR){ req.tv_sec = rem.tv_sec; req.tv_nsec = rem.tv_nsec; }else{ perror("nanosleep error"); return -1; } } return 0; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
(プログラムの概要)
ユーザーが自由に定義してもよいSIGUSR1を使います。シグナルハンドラーの登録を行い、SIGUSR1が通知されるとSignalHandler()が起動されるように設定します。
シグナルハンドラーが起動される度にグローバル変数sigcntをカウントアップし、main関数内で1秒に1回起動回数を表示しています。
シグナルハンドラーではカウントアップ後に無駄な処理を行い処理に時間がかかるようにしてあります。
#! /bin/bash #input : PID cnt=0 while [ $cnt -lt 10000 ]; do kill -SIGUSR1 $1 cnt=`expr $cnt + 1` done
1 2 3 4 5 6 7 8 9
引数として渡されるPIDのプロセスに対してSIGUSR1を1万回送信します。
(動作)
gcc -Wall sigusr1.c
コンパイルしたプログラムを起動させます。ps -aコマンドでプロセスIDを取得し、スクリプトを実行してみてください。
どうでしょう?シグナルハンドラーは1万回起動されましたか?環境にも依ると思いますが、スクリプトで送信した回数起動されていないと思います。
(うまく動かない理由)
まず、デフォルトの設定ではシグナルハンドラー実行中はそのシグナルがマスクされています。(つまりシグナルハンドラー実行中に同じシグナルが通知されてもシグナルハンドラーが二重で起動されません。)
そして、マスクされている状態の時通知されたシグナルはキューイングされ、ハンドラー終了後に改めてシグナルハンドラーが起動されます(pending状態)。しかし、標準シグナルの場合1つしかキューイングされないため、シグナルハンドラー実行中に2回以上シグナルが通知されると残りが欠けてしまう為にこの様な現象になってしまいます。
これに対してリアルタイムシグナルは複数のシグナルをキューイングすることができます。キューイングできる数はkernel2.6.8以降はulimit -iコマンド、またはulimit -aで表示されるpending signalsで調べることができます。2.6.7以前はシステム全体でキューイングできる数が制限されており、/proc/sys/kernel/rtsig-maxで調べることができます。
次回リアルタイムシグナル2はこのプログラムをリアルタイムシグナル化し、シグナルの取りこぼしがないことを確認します。