Linuxにおけるシグナルの仕組みと実装

1. シグナルの基本概念

シグナルは、カーネルがユーザー空間のプロセスに送信する非同期通知機構であり、特定のイベント発生を知らせるために利用される。

1.1 シグナルの処理フロー

  1. 生成:カーネル、他のプロセス、またはハードウェア(例:MMUによる`SIGSEGV`)がシグナルを発生させる。
  2. 配信:カーネルが対象プロセスにシグナルを伝達し、処理を促す。
  3. 処理:プロセスがシグナルを受け取り、事前に定義された動作を実行する。

1.2 シグナルの発生源

  • ハードウェア異常:
    • SIGSEGV:無効なメモリアクセス(セグメンテーション違反)
    • SIGFPE:算術エラー(ゼロ除算など)
    • SIGILL:無効な命令実行
  • 端末操作:
    • SIGINT:Ctrl+Cによる中断
    • SIGQUIT:Ctrl+\による終了とコアダンプ生成
    • SIGTSTP:Ctrl+Zによる一時停止
  • ソフトウェアイベント:
    • SIGCHLD:子プロセスの状態変化(終了・停止)
    • SIGPIPE:読み込み側が閉じられたパイプへの書き込み
    • SIGALRM:タイマー(alarm/setitimer)の期限切れ
    • SIGUSR1, SIGUSR2:ユーザー定義シグナル
  • 他プロセスからの送信:kill()システムコール経由で発行

1.3 シグナルの処理戦略

  • デフォルト動作:多くのシグナルはプロセス終了を引き起こす。一部は無視(SIGCHLD)やコアダンプ(SIGQUIT)。
  • 無視:内核に処理不要と通知(SIGKILL/SIGSTOPは無視不可)。
  • キャッチ:シグナルハンドラ関数を登録。到着時に処理を中断してハンドラを実行。

1.4 主要なシステムコール

  • signal():簡易なハンドラ登録(非推奨、互換性劣る)
  • sigaction():標準的かつ強力なハンドラ設定方法
  • kill():指定プロセスへシグナル送信
  • alarm():タイムアウト後にSIGALRMを発生
  • pause():シグナル受信までプロセスを待機
  • sigemptyset(), sigaddset(), sigprocmask():シグナルマスク管理

1.5 注意点

  • 非同期性:任意のタイミングで到着可能
  • 信頼性:標準シグナル(1-31)は不可靠(再発生時は無視)。リアルタイムシグナル(34-64)はキューイング可能
  • 再入安全性:ハンドラ内ではwrite()や_exit()など、非同期信号安全な関数のみ使用可。malloc()やprintf()は避ける
  • volatile宣言:ハンドラとメインコード間の共有変数はvolatile修飾子を付与

2. シグナルの分類と用途

2.1 終了シグナル

シグナル値デフォルト説明捕獲/無視可否コアダンプ
SIGHUP1終了端末切断時、親プロセスグループに送信。リロード通知にも利用是否
SIGINT2終了Ctrl+Cによる中止。前向きな終了を促す是否
SIGQUIT3コアダンプCtrl+\で終了+ダンプ生成。デバッグ用是是
SIGTERM15終了killのデフォルト。クリーンアップ可能な終了是否
SIGKILL9終了強制終了。無視・捕捉不可。最後の手段否否

2.2 異常シグナル

シグナル値デフォルト説明捕獲/無視可否コアダンプ
SIGILL4コアダンプ無効な命令実行。実行ファイル破損など是是
SIGTRAP5コアダンプブレークポイントやトレーストラップ。デバッガ用是是
SIGABRT6コアダンププログラム自身がabort()呼出。アサート失敗でも発生是是
SIGBUS7コアダンプ物理アドレス不正・アライメント違反是是
SIGFPE8コアダンプゼロ除算・オーバーフロー是是
SIGSEGV11コアダンプ最も一般的なメモリアクセス違反。ポインタ誤用の兆候是是

2.3 作業制御シグナル

シグナル値デフォルト説明捕獲/無視可否
SIGSTOP17,19,23停止プロセス実行の一時停止。不可捕捉・不可無視否
SIGTSTP18,20,24停止Ctrl+Zによる停止。後で再開可能是
SIGCONT19,18,25継続停止したプロセスの再開。無視しても有効是

2.4 I/O関連シグナル

シグナル用途推奨処理
SIGPIPEパイプ断接後の書き込みエラー無視(SIG_IGN)し、EPIPEエラーを検査
SIGIOファイルディスクリプタの準備完了通知高負荷アプリではepoll優先
SIGURG帯外データ(OOB)処理緊急コマンド受信用にハンドラ設置
SIGCHLD子プロセスリソース回収子プロセス終了時にwaitpid()呼び出し必須

2.5 その他の重要シグナル

シグナル値デフォルト説明捕獲/無視可否
SIGCHLD17,20,18無視子プロセスの終了・停止時、親に通知。リソース回収に必須是
SIGPIPE13終了読端が閉じられたパイプへの書き込み是
SIGALRM14終了alarm()やsetitimer()によるタイマー切れ是
SIGUSR110,30,16終了ユーザー定義1。用途はアプリケーションで定義是
SIGUSR212,31,17終了ユーザー定義2。ログ切り替え・状態報告などに利用是

3. 実装例

3.1 SIGPIPEの回避

ネットワークサーバーでクライアント切断後に応答しようとした場合、send()がSIGPIPEを送出しプロセスが終了する。

// 初期化時に無視
#include <signal.h>
signal(SIGPIPE, SIG_IGN);

// 以降、I/O関数はエラー返却
ssize_t sent = send(sockfd, data, len, MSG_NOSIGNAL);
if (sent == -1 && errno == EPIPE) {
    close(sockfd); // リソース解放
}

3.2 SIGIOによる非同期I/O

イベント駆動型の非同期処理を実現する。

#include <unistd.h>
#include <fcntl.h>
#include <signal.h>

void io_callback(int sig, siginfo_t *info, void *context) {
    int fd = info->si_fd;
    int event = info->si_band;
    // ファイルディスクリプタの準備完了処理
}

int main() {
    struct sigaction sa = {0};
    sa.sa_sigaction = io_callback;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO;

    sigaction(SIGIO, &sa, NULL);

    int fd = open("/dev/device", O_RDONLY);
    fcntl(fd, F_SETOWN, getpid());
    fcntl(fd, F_SETFL, O_ASYNC | fcntl(fd, F_GETFL));

    // 他の処理...
}

注:現代のネットワークアプリではepollの方が性能的に優れている。

3.3 SIGURGによる緊急コマンド受信

ネットワーク経由で「緊急停止」などの緊急指令を受信。

// 送信側
send(sockfd, "!", 1, MSG_OOB);

// 受信側
void urg_handler(int sig) {
    char urgent_data;
    recv(sockfd, &urgent_data, 1, MSG_OOB);
    // 緊急処理
}

// シグナル登録
signal(SIGURG, urg_handler);
fcntl(sockfd, F_SETOWN, getpid());

3.4 SIGCHLDによる子プロセス管理

並列サーバーで子プロセスが終了した際にゾンビ化を防ぐ。

void child_reaper(int sig) {
    int saved_errno = errno;
    while (waitpid(-1, NULL, WNOHANG) > 0) {
        continue;
    }
    errno = saved_errno;
}

int main() {
    struct sigaction sa = {0};
    sa.sa_handler = child_reaper;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;

    sigaction(SIGCHLD, &sa, NULL);

    while(1) {
        pid_t pid = fork();
        if (pid == 0) {
            // 子プロセス処理
            exit(0);
        }
        // 親プロセスはループ継続
    }
}

タグ: linux シグナル C言語 システムプログラミング 非同期処理

9月26日 19:07 投稿