導入
シグナルシリーズ第1弾です。目次は こちら
シグナルをハンドルするといえば、signalだよね、ということで。
動作確認環境
Host OS: Windows10 Pro 20H2
Guest OS: WSL2 Ubuntu-20.04
Compiler: g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
注意
後から書いても私なら読まないので先に注意事項を記載します。
以下に代表的な注意事項を示しますが、他にもあります。(JPCERTのページを参照)
シグナルハンドラからは非同期安全な関数のみをコールしてください。
JPCERT CC: SIG30-C
共有オブジェクトにはアクセスしない(volatileなsig_atomic_t型変数は可)
JPCERT CC: SIG31-C
また、signal() は非推奨です。
移植性がないのでsigactionを使用すること。詳細はman pageを見てください。
signal
みんな大好き非推奨のsignalです。
man page: signal(2)
signalの第2引数に signal_handlerではなく SIG_DFL を指定すればデフォルト動作、 SIG_IGN を指定すれば無視します。
動作確認コード
#include <stdio.h>
#include <stdlib.h>
#include <signal.h> // signal
#include <unistd.h> // usleep
#define LOG(STR, ...) printf("%s " STR "\n", __func__, ## __VA_ARGS__)
// ハンドラ
volatile sig_atomic_t g_signal = 0;
void signal_handler(int signo) { g_signal ++; }
int main() {
LOG("start");
// ハンドラを登録する
if( signal(SIGINT, signal_handler) == SIG_ERR ) {
LOG("err signal");
return 1;
}
// シグナル待ち
volatile sig_atomic_t old = g_signal;
int cnt = 0;
char mark[5] = {'|', '/', '-', '\\'};
while(true) {
// ハンドルチェック
if( g_signal > old ) {
LOG("signal %d", g_signal);
old = g_signal;
if( g_signal >= 3 ) {
break; //3回目をハンドルしたら終了
}
}
// アプリが止まっていないことの確認用
printf("%c\r", mark[(cnt + 1) % 4]); fflush(stdout); cnt++;
usleep(200 * 1000);
}
LOG("end");
return 0;
}
sigaction
こっちが推奨です。
man page: sigaction(2)
(1) signal互換
まずは、signalと互換の動作です。
sa.sa_flags に SA_SIGINFO を指定しなければ sa.sa_handler を使います。
動作確認コード
#include <stdio.h>
#include <stdlib.h>
#include <signal.h> // sigaction
#include <string.h> // memset
#include <unistd.h> // usleep
#define LOG(STR, ...) printf("%s " STR "\n", __func__, ## __VA_ARGS__)
// ハンドラ
volatile sig_atomic_t g_signal = 0;
void signal_handler(int signo) { g_signal ++; }
int main() {
LOG("start");
// ハンドラを登録する
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = signal_handler;
if( sigaction(2, &sa, nullptr) < 0 ) {
LOG("err signal");
return 1;
}
// シグナル待ち
volatile sig_atomic_t old = g_signal;
int cnt = 0;
char mark[5] = {'|', '/', '-', '\\'};
while(true) {
// ハンドルチェック
if( g_signal > old ) {
LOG("signal %d", g_signal);
old = g_signal;
if( g_signal >= 3 ) {
break; //3回目をハンドルしたら終了
}
}
// アプリが止まっていないことの確認用
printf("%c\r", mark[(cnt + 1) % 4]); fflush(stdout); cnt++;
usleep(200 * 1000);
}
LOG("end");
return 0;
}
(2) 拡張
sa.sa_flags に SA_SIGINFO をすると sa.sa_sigaction を使います。
動作確認コード
#include <stdio.h>
#include <stdlib.h>
#include <signal.h> // sigaction
#include <string.h> // memset
#include <unistd.h> // usleep
#define LOG(STR, ...) printf("%s " STR "\n", __func__, ## __VA_ARGS__)
// ハンドラ
volatile sig_atomic_t g_signal = 0;
volatile sig_atomic_t g_signo = 0;
volatile sig_atomic_t g_errno = 0;
volatile sig_atomic_t g_code = 0;
void sigaction_handler(int signo, siginfo_t *info, void *ctx) {
g_signal ++;
// 第2引数 info は以下の3要素はすべてのシグナルで定義される。
// その他のフィールドはシグナルに応じて定義される。
g_signo = info->si_signo;
g_errno = info->si_errno; // linuxでは一般的に使用されない
g_code = info->si_code;
}
int main() {
LOG("start");
// ハンドラを登録する
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = sigaction_handler;
sa.sa_flags = SA_SIGINFO;
if( sigaction(2, &sa, nullptr) < 0 ) {
LOG("err signal");
return 1;
}
// シグナル待ち
volatile sig_atomic_t old = g_signal;
int cnt = 0;
char mark[5] = {'|', '/', '-', '\\'};
while(true) {
// ハンドルチェック
if( g_signal > old ) {
LOG("signal %d %d %d %d", g_signal, g_signo, g_errno, g_code);
old = g_signal;
if( g_signal >= 3 ) {
break; //3回目をハンドルしたら終了
}
}
// アプリが止まっていないことの確認用
printf("%c\r", mark[(cnt + 1) % 4]); fflush(stdout); cnt++;
usleep(200 * 1000);
}
LOG("end");
return 0;
}