読者です 読者をやめる 読者になる 読者になる

すがブロ

sugamasaoのhatenablogだよ

SIGNAL を受信する(2)

前回の続き

前回(SIGNAL を受信する - @sugamasao.blog.title # => ”コードで世界を変えたい”)があんまりにも投げっぱなしだったので、補足しておく。
シグナル後の動きを分かりやすくする為に、少しプリント文等を追加している。

ソース

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

#define SIGNAL_ON 0
#define SIGNAL_OFF 1

int is_signal_int = SIGNAL_OFF;
int is_signal_term = SIGNAL_OFF;

/************************************
 * SYGINT 用関数
 * **********************************/
void signal_int(int signum, siginfo_t *info, void *ctx){
        printf("signal[%d] signo(%d) code(0x%x)\n", signum, info->si_signo, info->si_code);
        is_signal_int = SIGNAL_ON;
}

/************************************
 * SYSTERM 用関数
 * **********************************/
void signal_term(int signum, siginfo_t *info, void *ctx){
        printf("signal[%d] signo(%d) code(0x%x)\n", signum, info->si_signo, info->si_code);
        is_signal_term = SIGNAL_ON;
}

int main(void) {
        struct sigaction signal[2];
        memset(&signal, 0, sizeof(signal));

        // シグナルハンドラ設定: SIGINT
        signal[0].sa_sigaction = signal_int;
        signal[0].sa_flags = SA_RESTART | SA_SIGINFO;

        if (sigaction(SIGINT, &signal[0], NULL) < 0) {
                exit(2);
        }

        // シグナルハンドラ設定: SIGTERM
        signal[1].sa_sigaction = signal_term;
        signal[1].sa_flags = SA_RESTART | SA_SIGINFO;

        if (sigaction(SIGTERM, &signal[1], NULL) < 0) {
                exit(2);
        }

        // シグナルで停止するまで無限ループ
        while(1) {
                puts("while[start]...");

                puts("ココの処理はかならずやり遂げるのだ!");
                sleep(1);
                puts("1秒...");
                sleep(1);
                puts("2秒...");
                sleep(1);
                puts("3秒...");

                // SIGNAL を受けたかチェック
                if(is_signal_term == SIGNAL_ON || is_signal_int == SIGNAL_ON) {
                        puts("終了しまーす");
                        exit(1);
                }
                puts("while[end]...");
                sleep(10);
        }

        return 0;
}

解説

シグナルハンドラとして、自前で定義した signal_xxxx 関数を各シグナルに割り当てる。ここら辺は JavaScript でのシグナルハンドラの設定と同じ感覚ですね。
で、その後、シグナルを受けて即時に動作を変更したい場合等は上記の signal_xxxx に動きを書けば良いけれど、シグナルの受信処理とシグナル受信時の動作を非同期的に現したい場合・・・ソースで言うと、

puts("ココの処理はかならずやり遂げるのだ!");

の部分はどんなタイミングで SIGINT や SIGTERM を受け取ったとしても、必ずやり遂げたい。そうする場合、グローバルなシグナル受信フラグを使う事で、受信とプログラム終了の処理を別のタイミングで実現している。
実際に動いていて、シグナルを受信した様子は下記になる

while[start]...
ココの処理はかならずやり遂げるのだ!
1秒...
2秒...
3秒...
while[end]...
^Csignal[2] signo(2) code(0x0) #<---------ココで SIGINT が発生
while[start]...
ココの処理はかならずやり遂げるのだ!
1秒...
2秒...
3秒...
終了しまーす

こんな感じ。
こうする事で、シグナル受信後にファイルのクローズ等をちゃんとケアして安全にプログラムを終了できる作りにしてあげることができる。