SIGINT ハンドラは一度だけ実行されます。

SIGINT ハンドラは一度だけ実行されます。

信号と非同期信号安全でない機能について学んでいます。特に、これはprintf非同期信号に対して安全ではなく、基本プログラムスレッドおよび信号ハンドラから呼び出されるとデッドロックを引き起こす可能性があることがわかりました。これを確認するために、次のプログラムを作成しました(少し見苦しい)。

/*
 * sig-deadlock.c
 */

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

void
sigint_handler(int signum)
{
        int i;
        printf("Signal");
        for (i = 0; i < 30; i++) {
                printf("%d\n", i);
        }
}

int
main(void)
{
        int i;
        printf("PID: %d\n", getpid());
        signal(SIGINT, sigint_handler);
        for (i = 0; i < 1e9; i++) {
                printf("a");
                sleep(1);
        }

        return EXIT_SUCCESS;
}

私はプログラムが内部ループを実行し(したがって呼び出すprintf)、SIGINTを送信するたびに(使用してkill -INT $PID)30個のシグナルハンドラを実行したいと思います。

しかし、実行中にシグナルハンドラが一度実行され、次のシグナルがプロセスを終了することが観察されました。この動作の原因と解決策は何ですか?

オペレーティングシステム:Linux 4.9.0。

ベストアンサー1

signal(2)これは以前のシグナルAPIの一部であり、常に使用するのに便利ではありません。主に次の理由による。

signal()の唯一のポータブル使用は、信号の処理をまたはSIG_DFLに設定することですSIG_IGN。シグナルハンドラを設定するときに使用される意味はsignal()システムによって異なります(そしてPOSIX.1はこれらの変形を明示的に許可します)。この目的で使用しないでください。

もともとUNIXシステムでは、signal()シグナリングを介してハンドラが呼び出されたとき、信号処理が次にリセットされる。SIG_DFL、システムは追加のシグナルインスタンスをブロックしません。システムVはまだsignal()

BSDでは、シグナルハンドラが呼び出されるとシグナル処理はリセットされず、ハンドラの実行中にシグナルの他のインスタンスが転送されないようにブロックされます。

Linuxの状況は次のとおりです。

  • カーネルのsignal()システムコールはSystem Vセマンティクスを提供します。
  • デフォルトでは、glibc 2以降では、signal()ラッパー関数[...]がsigaction(2)BSDセマンティクスを提供するフラグとともに呼び出されます。
  • glibc 2以降では、_BSD_SOURCE機能テストマクロが定義されていない場合、signal()System Vセマンティクスが提供されます。 (標準モードのいずれかで呼び出されると、デフォルトの暗黙的な_BSD_SOURCE 定義は提供されません。または...参照gcc(1)-std=xxx-ansisignal(2)

つまり、多くの場合、設定されたハンドラは複数回使用されません。シグナルの後に定義した関数は削除され、プロセスが終了すると、signalハンドラはデフォルト値にリセットされます。SIGINT

引用したばかりのマニュアルページを読むことをお勧めしますが、この状況を処理する最善の方法は使用を中止してsignalに切り替えることsigactionです。マニュアルページから必要なすべての詳細を取得できますが、以下は簡単な例です。

void sighandler(int signum);

int main(void) {
    struct sigaction sa;
    sa.sa_handler = sighandler;
    sigemptyset(&(sa.sa_mask));
    sigaddset(&(sa.sa_mask), SIGINT);
    sigaction(SIGINT, &sa, NULL);

    int i;
    for(i = 0; i < 5; i++) {
        printf("Sleeping...\n");
        sleep(5);
        printf("Woke up!\n");
    }
}

void sighandler(int signum) {
    printf("Signal caught!\n");
}

ただし、重要な注意事項があります。すでにわかっているように(したがって関数の「無限」ループ)、シグナルはプログラムがシグナルを処理するかどうかにかかわらず、シグナルが受信されるとシステムコールmain(コールなど)を中断します。sleep(3)上記の例では、プログラムは〜する印刷起こった!これを行うたびに、kill睡眠コールは早期に終了します。マニュアルページを見ると、通話が中断したときに残りの省電力時間(秒単位)を返す必要があることがわかります。

おすすめ記事