hana_shinのLinux技術ブログ

Linuxの技術情報を掲載しています。特にネットワークをメインに掲載していきます。

シグナルの受信処理について



1 はじめに

シグナルの受信処理について、以下のことを確認してみます。
・同じシグナルを複数回受信した場合の挙動
・通常シグナル、リアルタイムシグナルの受信処理の順序

シグナルについて以下の記事も書きました。
シグナルについて - hana_shinのLinux技術ブログ

2 検証環境

AlmaLinux版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
AlmaLinux release 9.1 (Lime Lynx)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
5.14.0-162.6.1.el9_1.x86_64

3 テストプログラム

テストプログラムの内容は次のとおりです。
SIGHUP(1)、SIGINT(2)、SIGRTMAX-1(63)、SIGRTMAX(64)の各シグナルについて、シグナルハンドラを登録し、それらシグナルを受信した際にはシグナル番号を表示します。シグナルハンドラでは、fprintf関数やprintf関数ではなく、シグナルセーフなwriteシステムコールを使ってメッセージを出力します。また、main関数ではfprintfの出力先にstderrを指定しています。これは、メッセージ出力をバッファリングせず、即座に端末に出力できるようにするためです。

[root@server tp]# cat signal.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

void sig_int(int sig) {
    char msg[100];
    snprintf(msg, sizeof(msg), "Signal number:%d\n", sig);
    write(STDERR_FILENO, msg, strlen(msg));
}

int main() {
    struct sigaction sa[4];
    int signals[] = {1, 11, 63, 64};

    for (int i = 0; i < 4; ++i) {
        sa[i].sa_handler = sig_int;
        sigemptyset(&sa[i].sa_mask);
        sa[i].sa_flags = 0;
        sigaction(signals[i], &sa[i], NULL);
    }

    fprintf(stderr, "PID=%d\n", getpid());
    while(1) {
        sleep(600);
    }
}

テストプログラムをコンパイルします。

[root@server tp]# gcc -Wall -o signal signal.c

4 同じシグナルを複数回受信した場合について

4.1 通常シグナルを複数回受信した場合

テストプログラムを実行します。

[root@server tp]# ./signal
PID=1189

Ctrl+zを押下して、signalプロセスを停止状態にします。なお、SIGSTOPシグナルをsignalプロセスに送信しても同じ状態になります。

[root@server tp]# ./signal
PID=1189
^Z
[1]+  停止                  ./signal

psコマンドを使用してsignalプロセスの状態を確認すると、停止状態(STATがT)であることがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1189 0000000000000000 0000000000000000 0000000000000000 c000000000000401 T    pts/1      0:00 ./signal

signalプロセスにSIGHUPシグナルを3つ送信します。

[root@server ~]# kill -SIGHUP 1189
[root@server ~]# kill -SIGHUP 1189
[root@server ~]# kill -SIGHUP 1189

psコマンドを実行して、signalプロセスの状態を確認します。PENDINGが0000000000000001であることがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1189 0000000000000001 0000000000000000 0000000000000000 c000000000000401 T    pts/1      0:00 ./signal

シグナルについて - hana_shinのLinux技術ブログに記載したプログラム(signal.py)を実行すると、SIGHUPシグナルが保留されていることがわかります。

[root@server ~]# ./signal.py
Please enter a hexadecimal number:0000000000000001
Active signals:
SIGHUP(1)

fgコマンドを実行して、signalプロセスを再開します。signalプロセスにSIGHUPシグナルを3つ送信したのですが、シグナルハンドラが1回しか実行されていないので、処理されシグナルは1回だけであることがわかります。

[root@server tp]# fg
./signal
Signal number:1

psコマンドを使用してsignalプロセスの状態を確認すると、PENDINGが0000000000000000であることがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1189 0000000000000000 0000000000000000 0000000000000000 c000000000000401 S+   pts/0      0:00 ./signal

テストCtrl+cを押下して、テストプログラムを終了します。

[root@server tp]# fg
./signal
Signal number:1
^C

4.2 リアルタイムシグナルを複数回受信した場合

テストプログラムを実行します。

[root@server tp]# ./signal
PID=1230

psコマンドを実行して、プロセスの状態を確認します。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1230 0000000000000000 0000000000000000 0000000000000000 c000000000000401 S+   pts/1      0:00 ./signal

Ctrl+zキーを押下して、signalプロセスを停止状態にします。

[root@server tp]# ./signal
PID=1230
^Z
[1]+  停止                  ./signal

psコマンドを使用してsignalプロセスの状態を確認すると、停止状態(STATがT)であることがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1230 0000000000000000 0000000000000000 0000000000000000 c000000000000401 T    pts/1      0:00 ./signal

signalプロセスにSIGRTMAXシグナルを3つ送信します。

[root@server ~]# kill -SIGRTMAX 1230
[root@server ~]# kill -SIGRTMAX 1230
[root@server ~]# kill -SIGRTMAX 1230

psコマンドを実行して、signalプロセスの状態を確認します。PENDINGが8000000000000000であることがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1230 8000000000000000 0000000000000000 0000000000000000 c000000000000401 T    pts/1      0:00 ./signal

シグナルについて - hana_shinのLinux技術ブログに記載したプログラムを実行すると、SIGRTMAXシグナルが保留されていることがわかります。

[root@server ~]# ./signal.py
Please enter a hexadecimal number:8000000000000000
Active signals:
SIGRTMAX(64)

fgコマンドを実行して、signal プロセスを再開します。シグナルハンドラが3回実行されたため、処理されたシグナルは3回であることがわかります。通常のシグナルとは異なり、リアルタイムシグナルは受信したシグナルの回数を実行することができます。

[root@server tp]# fg
./signal
Signal number:64
Signal number:64
Signal number:64

signalプロセスが再開して、保留中のシグナルを処理したので、PENDINGが0になったことがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1230 0000000000000000 0000000000000000 0000000000000000 c000000000000401 S+   pts/1      0:00 ./signal

5 通常シグナルとリアルタイムシグナルの優先度について

テストプログラムを実行します。

[root@server tp]# ./signal
PID=1321

Ctrl+zを押下して、signalプロセスを停止状態にします。

[root@server tp]# ./signal
PID=1321
^Z
[1]+  停止                  ./signal

signalプロセスにSIGHUP, SIGSEGV, SIGRTMAX-1, SIGRTMAX を送信します。SIGHUP, SIGSEGVは通常シグナルです。SIGRTMAX-1, SIGRTMAXはリアルタイムシグナルです。

[root@server ~]# kill -SIGHUP 1321
[root@server ~]# kill -SIGSEGV 1321
[root@server ~]# kill -SIGRTMAX-1 1321
[root@server ~]# kill -SIGRTMAX 1321

psコマンドを実行して、signalプロセスの状態を確認します。PENDINGがc000000000000401であることがわかります。

[root@server ~]# ps -C signal s
  UID     PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
    0    1321 c000000000000401 0000000000000000 0000000000000000 c000000000000401 T    pts/1      0:00 ./signal

signal.pyを実行すると、SIGHUP, SIGSEGV, SIGRTMAX-1, SIGRTMAXが保留されていることがわかります。

[root@server ~]# ./signal.py
Please enter a hexadecimal number:c000000000000401
Active signals:
SIGHUP(1), SIGSEGV(11), SIGRTMAX-1(63), SIGRTMAX(64)

fgコマンドを実行して、signal プロセスを再開します。リアルタイムシグナル、通常シグナルの順でシグナルが処理されていることがわかります。また、リアルタイムシグナルは番号の大きいものから、通常シグナルは番号の小さいものから処理されていることがわかります。

[root@server tp]# fg
./signal
Signal number:64
Signal number:63
Signal number:1
Signal number:11

6 まとめ

項目 説明
通常シグナルとリアルタイムシグナルの優先度 リアルタイムシグナルが通常シグナルより優先して処理されます
リアルタイムシグナル同士の優先度 番号の大きいリアルタイムシグナルが優先して処理されます
通常シグナル同士の優先度 番号の小さい通常シグナルが優先して処理されます
同じシグナルの実行回数 通常シグナルの場合、同じシグナルをN回受信しても処理するのは1回です。リアルタイムシグナルの場合、同じシグナルをN回受信したら、N回受信処理をします

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ