シグナルハンドラ

シグナルハンドラ内では、malloc()やprintf()系が使えず、thread safe な関数しか呼べないので、シグナルハンドラではキューに記録して、メインルーチン内でキューを取り出して処理する形式を考えてみた。

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <signal.h>
#include <ucontext.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#define TM_ONESHOT 1
#define TM_INTERVAL 0
#define QUEMAX 256

timer_t timerid;
volatile static int head = 0,tail=0;
volatile static int event_queue[QUEMAX];

static int sigint_sigevent(int signum, siginfo_t *info, void *ctx){
    int ret;

    ret = 0;

    if(signum>0){ // シグナルによる呼び出し
        event_queue[tail++] = signum;
        if(tail>=QUEMAX){
            tail = 0;
        }
        if(head==tail){
            head++;
            if(head>=QUEMAX){
                head = 0;
            }
        }
    }else{
        if(head!=tail){
            ret = event_queue[head++];
            if(head>=QUEMAX){
                head = 0;
            }
        }
    }

    return ret;
}
int set_signal_handler(void){
	struct sigaction sa;

	memset(&sa, 0, sizeof(sa));

	sa.sa_flags = SA_SIGINFO|SA_RESTART;
	sigemptyset(&sa.sa_mask); 

	sigaddset(&sa.sa_mask,SIGINT);
	sigaddset(&sa.sa_mask,SIGRTMIN+1); 

	sa.sa_sigaction = (void *)sigint_sigevent;
	if(sigaction(SIGINT,&sa,(struct sigaction *)NULL)) {
		perror("sigaction error");
		exit(1);
 	}
	sa.sa_sigaction = (void *)sigint_sigevent;
	if(sigaction(SIGRTMIN+1,&sa,(struct sigaction *)NULL)) {
		perror("sigaction error");
		exit(1);
 	}
	return 0;
}
int set_timer_count(int oneshot,int sec,int nsec){
	struct itimerspec ispec;

	if(oneshot!=TM_INTERVAL){
		ispec.it_interval.tv_sec = 0;
		ispec.it_interval.tv_nsec = 0;
	}else{
		ispec.it_interval.tv_sec = sec;
		ispec.it_interval.tv_nsec = nsec;
	}
	ispec.it_value.tv_sec = sec;
	ispec.it_value.tv_nsec = nsec;
	if(timer_settime(timerid, 0, &ispec, NULL) < 0){
		perror("timer_settime error");
		exit(1);
	}
	return 0;
}
int init_timer(void){
	struct sigevent se;

	memset(&se, 0, sizeof(se));
	se.sigev_notify = SIGEV_SIGNAL;
	se.sigev_signo = SIGRTMIN+1;
	if(timer_create(CLOCK_REALTIME, &se, &timerid) < 0){
		perror("timer_create error");
		exit(1);
	} 

	return 0;
}

int main(int argc, char** argv)
{
	init_timer();
	set_timer_count(TM_INTERVAL,0,500000000);
	set_signal_handler();

	while(1){
		printf("%d:%d sigevent:%d\n",head,tail,sigint_sigevent(0,NULL,NULL));
		while(usleep(50000)!=0);
	}
	return 0;
}

実際に動作させてみると、0.5秒毎にタイマーが起動してキューにシグナル番号が積まれるが、メインルーチンで、0.05秒毎にキューをチェックするので、キューに積まれるシグナル番号は殆ど増える事は無く処理される。

30:30 sigevent:0
30:30 sigevent:0
30:30 sigevent:0
30:30 sigevent:0
30:30 sigevent:0
30:30 sigevent:0
31:31 sigevent:35
31:31 sigevent:0
31:31 sigevent:0
31:31 sigevent:0
31:31 sigevent:0
31:31 sigevent:0
31:31 sigevent:0

実行すると、こんな感じ。

表示しているのは、
キュー先頭(head):キュー末尾(tail) sigevent:シグナル番号
というフォーマットになっている。

しかし、Ctrl-Cを押しっぱなしにすると、メインルーチンでは、usleep()から抜けないので、キューにSIGINTが積まれまくる。

130:130 sigevent:0
131:131 sigevent:35
^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C132:42 sigevent:2
133:42 sigevent:2
134:42 sigevent:2
135:43 sigevent:2
136:43 sigevent:2
137:43 sigevent:2
138:43 sigevent:2
139:43 sigevent:2
140:43 sigevent:2
141:43 sigevent:2
142:43 sigevent:2
143:43 sigevent:2
144:44 sigevent:2
145:44 sigevent:35

ちなみにこのキューはオーバーフローすると、古いデータを捨てるようになっている。オーバフローを確認する方法は作ってない(笑)。

Ctrl-Cを離すと、メインルーチンでキューに溜まっているデータをせっせと処理し、キューの先頭のheadの数字と末尾のtailの数字がみるみる接近していくのが見て取れる。

これで、メインルーチンでシグナル割り込みを検知し、malloc()、printf()使いたい放題。
難点は、ちょっとリアルタイム性が悪くなるという点だな。

このプログラムはリアルタイムライブラリを使うので、コンパイル時には -lrt オプションがいる。

【買い物】フルーツグラノラ

20091018_1

業務用スーパーへ行ったら、フルーツグラノラのデザインが一新されていて、いつものデザインを探して、一瞬、品切れかと思った。

印刷代が安そう :-)
業務用だから、実利が重要であって、虚飾は必要無いから、実に合理的だな。

棚にあるだけ9個全部購入。一応、店員に、「棚にあるだけしか無いのか?」と尋ねると、棚にあるだけとの返答で、今回は1ケースを注文しておいた。

ブログに書いておかないと、忘れてしまいそうだから、書いておく。