信号の保存と状態管理
信号は発生から処理までの間に状態を保持する必要があります。31個の通常信号については、int型変数で十分に状態を表すことができます。しかし、信号にはブロック状態など複数の状態が存在するため、OSはこれを構造化して管理します。
信号のライフサイクル
- 生成 (Produce): システムコールやハードウェア割り込みなどにより発生
- 保留中 (Pending): 生成から処理までの間の状態
- 配信 (Delivery): 実際にシグナルハンドラが実行される段階
信号の3つの状態テーブル
- blockテーブル: ブロックされている信号
- pendingテーブル: 保留中の信号
- handlerテーブル: 各信号の処理方法
blockテーブルとpendingテーブルはビットマップ構造で表現されます。各ビット位置が信号番号に対応し、0/1で状態を表します。
sigset_t型と信号操作関数
信号の状態を管理するための構造体sigset_tは、プラットフォーム非依存のビットマップ操作を提供します。
#include <signal.h>
// 信号セットの初期化
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
// 信号の追加/削除
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
// 信号の存在確認
int sigismember(const sigset_t *set, int signum);
信号マスクの操作
sigprocmask関数を使用して、現在のプロセスのブロック信号セットを操作できます。
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
howパラメータには以下のオプションがあります:
SIG_BLOCK: 現在のマスクにセットを追加SIG_UNBLOCK: マスクからセットを削除SIG_SETMASK: 新しいマスクを設定
保留中の信号確認
sigpending関数を使用して、現在保留中の信号を取得できます。
#include <signal.h>
int sigpending(sigset_t *set);
サンプルコード
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void print_pending(const sigset_t *pending) {
printf("Pending signals: ");
for(int i = 1; i <= 31; i++) {
if(sigismember(pending, i)) {
printf("1");
} else {
printf("0");
}
}
printf("\n");
}
int main() {
sigset_t mask, oldmask, pending;
// 初期設定:SIGINTをブロック
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigprocmask(SIG_BLOCK, &mask, &oldmask);
printf("SIGINT(SIG2)がブロックされました。5秒間この状態です。\n");
for(int i = 0; i < 5; i++) {
sigpending(&pending);
print_pending(&pending);
sleep(1);
}
// ブロック解除
sigprocmask(SIG_SETMASK, &oldmask, NULL);
printf("SIGINTのブロックが解除されました。\n");
while(1) {
sigpending(&pending);
print_pending(&pending);
sleep(1);
}
return 0;
}