シグナルハンドラについて
簡単にまとめておこうと思います。
そもそもシグナルとは何なのか・・・
シグナルは、あるイベント発生時に
それをプロセスに通知します。
何気なく使用している
"Ctrl + c"や"Ctrl + z"なども
すべてシグナルを発信しています。
killコマンドも実態は、
シグナルを発信させるコマンドです。
自身のプロセスに起因するシグナルを
同期シグナルと言い、
外部的要因によるシグナルを
非同期シグナルと言います。
シグナルの名前には全て、
"SIG"が付いています。
シグナルの概要が分かったところで、
具体的には何があるのでしょうか。
kill -l でシグナルを簡単に
表示させることが出来ます。
またC言語は標準で、
signal.hに定義されているので、
それを覗いてみてもよいでしょう。
もちろんシグナル1つ1つに
何らかの意味があります。

killコマンドに
オプションで番号を指定することで、
該当するシグナルをプロセスに
対して送信することが出来ます。
ちなみにkillコマンドを
オプションなしで使用すると、
シグナル15番のSIGTERMが
プロセスに送信されます。
よく強制終了させるために
使われるkill -9 は、
シグナル9番 SIGTSTP
ターミナルの強制停止です。
シグナルはイベントの通知です。
イベントが何か発生した際に、
そのイベント対して対処するコードを
プログラムに実装出来ます。
それがシグナルハンドラです。
シグナルがプロセスに届くと、
シグナルハンドラが割り込処理を行います。
しかしシグナルハンドラには、
処理できる内容に制限があります。
※後述します。
今回はシグナル2番 SIGINT
割り込みイベントが発生した際に、
プログラムを終了させます。
割り込みシグナルは、
Ctrl + C で、そのシグナルを
発生させることが出来ます。
もしくは、kill -2 コマンドです。
ソースはこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include<signal.h> #include<stdio.h> #include<sys/types.h> #include<unistd.h> volatile sig_atomic_t flag=0; void signal_handle(int signal){ //シグナルハンドラ。シグナル発生時の処理 flag = 1; } int main(void){ printf("Procss ID: %d\n", getpid()); signal(SIGINT, signal_handle); //シグナルの登録 while(!flag){} return 0; } |
getpid()でプロセスIDを
標準出力しています。
killコマンドでプロセスIDを
指定しやすいようにするためです。
それではこのコードを使って、
割り込みプログラムを終了させていきます。

killコマンドを送っても、
Ctrl + Cを押しても
プログラムは終了しました。
ちなみにシグナル2番SIGINTに対する
処理を実装していない場合は、
Ctrl + C を押しても、
プログラムは終了しません。
非同期シグナルセーフ関数とsig_atomic_t型変数
シグナルハンドラの中身を
書くときは注意が必要です。
それが非同期シグナルセーフ関数と
volatile修飾したsig_atomic_t型変数です。
非同期シグナルセーフ関数は、
ある種の関数グループです。
シグナルハンドラ割り込み処理で
安全に扱えるのは、
これに該当する関数のみです。
詳しい説明は長くなるので割愛しますが、
非同期シグナルセーフ関数でない
関数を用いると予期せぬエラーの
メモリ破壊などの原因になってしまいます。
非同期シグナルセーフ関数には、
以下に示す関数が該当します。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ちなみに最も良く使うであろう
printf関数は入っておりません。
なのでシグナルハンドラ内での使用は
控えた方が良いです。
最後にsig_atomic_t型変数です。
普通の変数を使用するより
こちらの変数を使用した方が安全です。
このアトミック(atomic)というのは、
何かの処理をする際に、
他から割り込みをされないことを保証
ということを意味しています。
さらにこの変数に
volatile修飾子を付けましょう。
volatile修飾子というのは、
よく組み込み系で使われるそうです。
volatileは、
変数へのアクセス最適化を
あえて無効化します。
プログラム上で変数を何回も
呼び出していても実際は
メモリから呼び出されていません。
レジスタに留めておいたものを使います。
つまり最適化してある場合は
変数を使いまわしています。
volatile修飾子はこの最適化を
意図的に無効するものです。
以上です。
上記の非同期シグナルセーフ関数と
sig_atomic型変数について
"なぜ"ということについて
あまり言及しませんでした。
こちらに詳しい説明が載っているので、
良ければ参考にしてみて下さい。
非同期シグナルセーフ関数について
https://www.jpcert.or.jp/sc-rules/c-sig30-c.html
sig_atomic型変数について
https://www.jpcert.or.jp/sc-rules/c-sig31-c.html
最後まで読んでいただきありがとうございました。