シグナルハンドラ内では、malloc()やprintf()系が使えず、thread safe な関数しか呼べないので、シグナルハンドラではキューに記録して、メインルーチン内でキューを取り出して処理する形式を考えてみた。
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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
#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 オプションがいる。