Linuxでは、さまざまなシグナルが扱われます。
今回は、そんなシグナルの中でもSIGALRM(シグアラーム)に関して、C言語のプログラムで扱う方法を紹介します。
SIGALRMの発生方法から、シグナルをキャッチする関数(シグナルハンドラ)の設定までを実装サンプル付きで説明していますのでぜひご覧ください。
C言語でSIGALRMを発報し、ハンドリングする方法を解説します
以下の記事では、AWSを無料で勉強する方法をまとめているので、あわせて参考にしてください。
上記の記事でも紹介していますが、侍テラコヤ
SIGALRMとは
SIGALRMとは、シグナルの1種で、alarm()関数などによって発報されるシグナルです。
アラームという名の通り、時間経過を教えてくれるシグナルとして使われます。
シグナルとは何か?に関しては以下の記事で解説しているので、あわせてご参照ください!
ちなみに、特に意識する必要はありませんが、SIGALRMの正体はsignal.hで整数14でdefineされた定数です。
(Macの場合は以下のパスにあります。 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/signal.h)
#define SIGALRM 14 /* alarm clock */
SIGALRMのシグナルハンドラを設定する方法
SIGALRMが何なのかについて簡単に触れたところで、実際にSIGALRMを発生させ、キャッチする(ハンドリングする)方法を紹介していきます。
SIGALRMを発生させる方法(alarm関数の使い方)
先述しましたが、SIGALRMシグナルはalarm()関数で発生させることができます。
alarmは、unistd.hをインクルードすると使用可能です。
alarm関数は、引数に正の整数値secondsを取り、seconds秒後経過したときにSIGALRMを配送する関数です。
戻り値は、前回のalarmで設定されたアラーム発報時間までの残り秒数を返します。
ちなみに、アラームは1つまでしか設定できないので、前回のアラームが完了する前にalarmを追加で呼び出すと、新しい方の秒数で上書きされてしまうので注意が必要です。
また、alarm(0)として引数を0にして呼び出すと、現在設定されているアラームをキャンセルできます。(SIGALRMの配送もなくなります)
設定されているアラームがない場合は0が返ります。
alarmの戻り値が0のときはアラームを設定し、0以外のときは設定しない、などの分岐が可能です。
ちなみに、sleep()関数はalarm()関数を使っている可能性があるため、こられは同時に使うのは避けた方がいいとマニュアルに記載がありますので気をつけましょう。
sleep(3) は SIGALRM を利用して実装されているかもしれない。 alarm() と sleep(3) を混ぜて使用してはならない。
https://linuxjm.osdn.jp/html/LDP_man-pages/man2/alarm.2.html
では、今までの内容をまとめた実装を載せておきます。
#include <unistd.h>
#include <stdio.h>
int main(void){
int ret;
ret = alarm(10); // 10秒後にSIGALRMが配送されるようにアラームセット
printf("ret = %d\n", ret); // 「0」と出力
ret = alarm(5); // 5秒後にアラームセット(10秒後のアラームはキャンセル)
printf("ret = %d\n", ret); // 「10」と出力
ret = alarm(0); // 5秒後のアラームをキャンセル(アラームなし)
printf("ret = %d\n", ret); // 「5」と出力
return 0;
}
SIGALRMのシグナルハンドラの設定方法
続いてSIGALRMが配送されたときに呼び出される関数(シグナルハンドラ)の設定です。
シグナルハンドラの設定方法はsignal関数を使う方法と、sigaction関数を使う方法の2種類があります。
今回は簡単に使えるsignal関数を使う方法で実装します。
signal、sigactionの使い方については以下の記事で解説しています。
signal関数の第一引数にSIGALRM、第二引数にシグナルハンドラにしたい関数名を指定します。
今回は、SIGALRMが配送されたときに、「時間です!」と出力するハンドラを実装します。
シグナルハンドラにできる関数は戻り値なし、引数intの関数です。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
/* シグナルハンドラ */
void alarm_handler(int signum){
puts("時間です!");
}
int main(void){
/* シグナルハンドラの設定 */
signal(SIGALRM, alarm_handler);
/* アラームの設定 */
alarm(5);
return 0;
}
しかし、上のプログラムではシグナルハンドラの設定はできますが、5秒後にアラームが発報される前にプログラムが終了していまいます。
シグナルハンドラの起動を確認するプログラム
先ほどのプログラムでは、シグナルハンドラを設定しましたが、SIGALRMが発報される前にプログラムが終了していしましました。
次は、アラームが発報されるまで無限ループをしてシグナルハンドラの動作を確認します。
フラグが0の間、無限ループをして、シグナルハンドラ内でフラグを立てることで無限ループから抜けます。
無限ループから抜けるときは「アラームが鳴りました!」と出力することで、シグナルハンドラ内でフラグが立ったことを確認できます。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int flag = 0;
/* シグナルハンドラ */
void alarm_handler(int signum){
puts("時間です!");
flag = 1;
}
int main(void){
/* シグナルハンドラの設定 */
signal(SIGALRM, alarm_handler);
/* アラームの設定 */
alarm(5);
/* アラームが発報されるまで無限ループ */
while(1){
if(flag > 0){
puts("アラームが鳴りました!");
break;
}
}
return 0;
}
このプログラムを実行すると、5秒後に以下のような出力が得られます。
時間です!
アラームが鳴りました!
alarmの引数を変えると、「時間です!」と表示されるまでの時間が変わりますのでぜひ試してみてください!
まとめ
今回はSIGALRMと、SIGALRMのシグナルハンドラについて説明しました。
以下、内容をまとめておきます。
- SIGALRMは、alarm関数によって配送(発報)されるシグナル
- alarm関数は、引数で設定した秒数後にSIGALRMを配送する
- alarm関数で設定できるアラームは1つまでで、前のアラームが発報以前にalarm関数を呼び出すとアラームが上書きされる
- alarm関数の戻り値は、前回設定したアラームが発報されるまでの残り秒数(前回のアラームがなければ0)
- alarm(0)とすると、すべてのアラームがキャンセルされる
以下の記事では、AWSを無料で勉強する方法をまとめているので、あわせて参考にしてください。
上記の記事でも紹介していますが、侍テラコヤ
コメント