[C言語 標準ライブラリ] signal.hの使い方

※本ブログでは、商品・サービスのリンク先にプロモーションを含みます。

スポンサーリンク

signal.hには、シグナル処理関数の登録およびシグナルの送信に関する型、マクロ、関数が宣言、定義されています。

スポンサーリンク

マクロ定数

SIG_DFL

シグナルに対する既定の操作を指示します。
signal関数の第2引数で使用されています。

SIG_ERR

エラーが発生したことを示します。
signal関数の戻り値で使用されます。

SIG_IGN

シグナルを無視することを指示します。
signal関数の第2引数で使用されます。

SIGABRT

異常終了を示すシグナル番号です。

SIGFPE

誤った算術演算(0除算、オーバーフロー等)を示すシグナル番号です。

SIGILL

不正な関数イメージの検出(不正命令等)を示すシグナル番号です。

SIGINT

対話的なアテンションシグナルの受け取りを示すシグナル番号です。

SIGSEGV

記憶域への不正なアクセスを示すシグナル番号です。

SIGTERM

プログラムへ送信された終了要求を示すシグナル番号です。

sig_atomic_t

シグナルハンドラから安全にアクセスできるオブジェクトの型です。
シグナルハンドラが呼び出されるとき、通常の関数とは異なる制約があります。特に、シグナルハンドラが割り込みを受ける可能性があり、その結果、複数のスレッドやシグナルハンドラが同時に同じオブジェクトにアクセスする可能性があります。sig_atomic_t型の変数は、このような同時アクセスが安全に行われることを保証します。

使用例:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

volatile sig_atomic_t flag = 0;

// SIGINTに対するハンドラ関数
void sigint_handler(int sig_num)
{
    flag = 1;
}

int main(void)
{
    // SIGINTに対するハンドラを設定する
    if (signal(SIGINT, sigint_handler) == SIG_ERR) {
        printf("SIGINTハンドラのセットアップに失敗しました。\n");
        return 1;
    }

    printf("SIGINTを待っています。\n");

    // flagがセットされるまでループします
    while(!flag){
    }

    printf("SIGINT信号をキャッチ!\n");

    return 0;
}

このプログラムは、SIGINTシグナル(通常はCtrl+Cによって発生)が受け取られるとその事を示すフラグをセットし、そのフラグがセットされたときにメインループを終了します。フラグはsig_atomic_t型で宣言されているため、シグナルハンドラ内から安全にアクセスすることができます。

実行結果:(実行後、Ctrl+CでSIGINT信号を送信)

SIGINTを待っています。
SIGINT信号をキャッチ!

関数

void (*signal(int sig, void (func)(int)))(int)

sigで指定されたシグナル番号に応じて、funcで指定された処理を実行します。
シグナル番号は以下の6つです。

  • SIGABRT(異常終了)※例えば、abort関数が発生させる。
  • SIGFPE(誤った算術演算)※例えば、0除算やオーバフローを発生する演算。
  • SIGILL(不正な関数イメージの検出)※例えば、不正命令。
  • SIGINT(対話的なアテンションシグナルの受け取り)
  • SIGSEGV(記憶域への不正なアクセス)
  • SIGTERM(プログラムへ送信された終了要求)

また、上記以外でも、追加のシグナルが処理系によって定義される場合があります。
signal関数は、funcにSIG_DELが指定されると、そのシグナルに対するデフォルトの操作を行います。
funcにSIG_IGNが指定されると、そのシグナルを無視します。
funcにそれ以外の値が指定された場合は、そのシグナルが発生した時に、funcを関数として呼び出します。
指定された操作が処理できる場合、指定されたシグナルsigに対して最も新しく成功したsignal関数呼出しのfuncの値が返されます。
そうでない場合は、SIG_ERRの値(正の値をerrnoに格納する)が返されます。

使用例:
SIGINT(通常はCtrl+Cを押して送信される)がプログラムに送信されたときに実行されるシグナルハンドラを設定します。

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

// SIGINTに対するハンドラ関数
void sigint_handler(int sig_num)
{
    printf("\nSIGINT信号をキャッチ!\n");

    // 通常はプログラムを終了させる
    exit(0);
}

int main(void)
{
    // SIGINTに対するハンドラを設定する
    if(signal(SIGINT, sigint_handler) == SIG_ERR){
        printf("SIGINTハンドラのセットアップに失敗しました。\n");
        return 1;
    }

    printf("SIGINT信号を待っています。\n");

    // 無限ループでSIGINTを待つ
    while(1){
    }

    return 0;
}

このプログラムは、SIGINTが送信されるまで無限ループを実行します。SIGINTが送信されると(通常はCtrl+Cを押すことで発生します)、sigint_handler関数が呼び出され、プログラムは終了します。

実行結果:(実行後、Ctrl+CでSIGINT信号を送信)

SIGINT信号を待っています。
SIGINT信号をキャッチ!

int raise(int sig)

シグナルsigを送信します。
シグナル処理ルーチンを呼び出した場合は、その実行が終了するまでraise関数から制御が戻ることはありません。
成功したときは0、失敗したときは0以外の値が返されます。

使用例:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

// SIGINTに対するハンドラ関数
void sigint_handler(int sig_num)
{
    printf("\nSIGINT信号をキャッチ!\n");

    // 通常はプログラムを終了させる
    exit(0);
}

int main(void)
{
    // SIGINTに対するハンドラを設定する
    if(signal(SIGINT, sigint_handler) == SIG_ERR){
        printf("SIGINTハンドラのセットアップに失敗しました。\n");
        return 1;
    }

    printf("SIGINTを発生させます。\n");

    // SIGINTシグナルを発生させます
    if(raise(SIGINT) != 0){
        printf("SIGINT信号の発生に失敗しました。\n");
        return 1;
    }

    printf("この行は実行されません。\n");

    return 0;
}

実行結果:

SIGINTを発生させます。
SIGINT信号をキャッチ!
スポンサーリンク
C言語

コメント