264
274

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Linux シグナルの基本と仕組み (カーネル v5.5 時点)

Last updated at Posted at 2020-05-24

この記事は、Linux シグナルの基本と仕組み (カーネルでの実装) について調査したことのまとめです。

シグナルは普段から利用しているものの仕組みについては理解してなかったので、カーネル勉強の題材として調べてみました。想像以上に複雑でボリュームがあったため、書き切れていない部分 (調査しきれなかった部分) もありますが、一通りの流れ (仕組み) は理解できたと思います。

なお、この記事は主に「■ 基本編」と「■ カーネル編 (v5.5)」で構成されています。仕組みを理解するには基本も知る必要があると思い、このような構成となっています。書籍レベルの基本を理解されている方は 「■ カーネル編 (v5.5)」 から読み進めることを推奨します。

■ 基本編

はじめにシグナルの基本について、ざっと整理します。

なお、例で登場するコマンドや API (C 言語) の細かい使い方やエラー処理などは省きます。詳細は man や参考文献の情報等をご活用ください。

1. シグナルとは

プロセスやプロセスグループへ様々なイベントを通知するためにあるカーネルの機能です (ソフトウェア割り込み)。イベントの通知は様々な場所 (自分/他プロセス、カーネル) から行うことが可能で、次のようなことができます。

  • ハングしたプロセスにシグナルを送信して強制終了させる
  • シグナルを送信してプロセスの処理を一時停止・再開させる
  • ハードウェア例外 (0 除算、メモリアクセス違反など) 時にシグナルを送信してプロセスを終了させる
  • シグナルを送信する特殊なキー (Ctrl + C など) を入力しプロセスを終了させる
  • シグナル受信時にユーザ定義の関数 (シグナルハンドラ) を実行させる

なお、シグナルは次のような流れで処理されます。詳細は追々見ていきます。

all_signal (2)-flow.jpg

2. シグナル利用例

イメージし易いように、ユーザ視点でのシグナル利用例を 3 つ記載します。

kill コマンド

kill コマンドでシグナル (SIGTERM) を送り sleep プロセスを終了させます。

$ ps
  PID TTY          TIME CMD
 2662 pts/0    00:00:00 bash
 2679 pts/0    00:00:00 sleep
 2685 pts/0    00:00:00 ps

$ kill 2679
[1]+  Terminated              sleep 1000

$ ps
  PID TTY          TIME CMD
 2662 pts/0    00:00:00 bash
 2696 pts/0    00:00:00 ps

割り込みキー

割り込みキー (Ctrl + C) でシグナル (SIGINT) を送り、無限ループするコマンド (プロセスグループ) を終了させます。

$ while :; do sleep 1; echo "Stop me !"; done
Stop me !
Stop me !
Stop me !
^C

プログラム (C 言語 API)

以下のように自前のプログラム (send_sig.c) でシグナル (SIGTERM) を送り sleep プロセスを終了させることもできます。

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

int main(int argc, char *argv[])
{
    pid_t pid = atoi(argv[1]);
    kill(pid, SIGTERM);

    return 0;
}

コンパイル & 実行例

$ ps
  PID TTY          TIME CMD 
30285 pts/0    00:00:00 bash
30597 pts/0    00:00:00 sleep
30631 pts/0    00:00:00 ps

$ gcc -o send_sig send_sig.c
$ ./send_sig 30597
[1]+  Terminated              sleep 1000

$ ps
  PID TTY          TIME CMD
30285 pts/0    00:00:00 bash
30663 pts/0    00:00:00 ps

3. シグナル番号とシグナル名

シグナルは、用途に応じて番号と名前が割り振られています。

$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX	

ただし、シグナル番号の一部はアーキテクチャに依存するため、上述の実行例 (x86_64) とは番号が異なる場合があります 1

4. 標準シグナル、リアルタイムシグナル

シグナルは、標準シグナルとリアルタイムシグナルの 2 種類に大別できます。

ざっくりとした違いは、

  • 標準シグナル ... SIGKILL など伝統的に使用しているシグナル (1 〜 31 番)
  • リアルタイムシグナル ... 標準シグナルの拡張版 (32 〜 64 番)

です。もう少し細かい違いは、次のとおりです。

標準シグナル リアルタイムシグナル
新/旧
シグナル名 シグナル毎に様々 SIGRTMIN(+n), SIGRTMAX(-n)
シグナル番号 1 〜 31 32 〜 64
標準動作 (詳細は後述) シグナル毎に様々 プロセス終了 (全シグナル)
用途 シグナル毎に様々 ユーザが定義する (全シグナル)
複数の同じシグナル
受信時の挙動
1 つのみ受信 全て受信
複数の同じシグナル
送信時の順序
規定なし 送信された順に到着
複数の異なるシグナル
送信時の順序
規定なし シグナル番号の小さい順に到着

なお、本稿ではこれ以上深くリアルタイムシグナルについては言及しません 1

5. シグナルアクション

シグナル受信時の動作 (シグナルアクション) には、次の 3 種類があります。この動作は後述の sigaction() などから変更できます。ただし、SIGKILL と SIGSTOP は標準動作以外にはできません。

all_signal-sigaction.jpg

  • 無視

    シグナルを受信しても何もしません。

    (設定するには、後述の sigaction() などで sa_handler に SIG_IGN を指定する)

  • 標準動作

    シグナル受信すると、シグナル毎に定義された標準動作 (後述の Term, Ign, Core, Stop, Cont) を実行します。

    (設定するには、後述の sigaction() などで sa_handler に SIG_DFL を指定 (デフォルト) する)

  • シグナルハンドラ

    シグナル受信すると、ユーザが定義した動作を実行します。

    (設定するには、後述の sigaction() などで sa_handler へユーザ定義の関数を指定する)

標準動作

標準動作は、シグナル毎に定義されており、次の 5 種類があります。

動作名 意味
Term プロセス終了
Ign 何もしない (sigaction() で設定できる SIG_IGN と同じ)
Core プロセス終了とコアダンプ生成 2
Stop プロセス一時停止 (TASK_STOPPED 状態に遷移させる)
Cont 一時停止したプロセスの再開 (TASK_STOPPED 状態からの復帰)

ただし、これは標準シグナルに限ります。リアルタイムシグナルの場合は Term のみです (次図参照)。

all_signal-kernel_handler.jpg

標準シグナルと対応する標準動作

1 から 31 番までの標準シグナルは、各々に紐づく標準動作を持ちます。

シグナル名 シグナル番号 (x86_64) 標準動作 意味
SIGHUP 1 Term 制御端末のハングアップ検出 or 制御プロセスの死
SIGINT 2 Term キーボードからの割り込み (Ctrl + C)
SIGQUIT 3 Core キーボードからの終了 (Ctrl + Q)
SIGILL 4 Core 不正な命令
SIGTRAP 5 Core デバッグ用のトレース/ブレークポイントシグナル
SIGABRT 6 Core abort(3) からの中断シグナル
SIGBUS 7 Core バスエラー (不正なメモリアクセス)
SIGFPE 8 Core 浮動小数点例外
SIGKILL 9 Term Kill シグナル
SIGUSR1 10 Term ユーザ定義のシグナル (その1)
SIGSEGV 11 Core 不正なメモリ参照
SIGUSR2 12 Term ユーザ定義のシグナル (その2)
SIGPIPE 13 Term パイプ破壊 (読み手の無いパイプへの書き出し) 3
SIGALRM 14 Term alarm(2) からのタイマーシグナル
SIGTERM 15 Term 終了シグナル
SIGSTKFLT 16 Term 数値演算プロセッサ (コプロセッサ)におけるスタックフォルト (未使用)
SIGCHLD 17 Ign 子プロセスの一時停止 (再開) または終了
SIGCONT 18 Cont 一時停止プロセスの再開
SIGSTOP 19 Stop プロセスの一時停止
SIGTSTP 20 Stop 制御端末からの停止 (Ctrl + Z)
SIGTTIN 21 Stop バッググラウンドプロセスが制御端末を読み取った
SIGTTOU 22 Stop バッググラウンドプロセスが制御端末へ書き込んだ
SIGURG 23 Ign ソケットに帯域外データ、緊急データが存在する
SIGXCPU 24 Core CPU 時間の上限 (RLIMIT_CPU) を超えた 4
SIGXFSZ 25 Core ファイルサイズの上限 (RLIMIT_FSIZE) を超えた 4
SIGVTALRM 26 Term 仮想タイマ (プロセスのユーザモードでの CPU 時間) がタイムアウトした
SIGPROF 27 Term プロファイリングタイマがタイムアウトした
SIGWINCH 28 Ign 制御端末のウィンドウサイズが変更された
SIGIO 29 Term 非同期 I/O イベント
SIGPWR 30 Term 電源の異常
SIGSYS 31 Core 不正なシステムコールを実行した 5

シグナルアクションの変更例

SIGKILLSIGSTOP 以外のシグナルアクションを変更するには、trap コマンド や sigaction() などの API (C 言語) を使用します。

trap コマンド

trap は Bash に組み込まれたビルトインコマンドの一種で、シグナルアクションを設定できます 6

ここでは、SIGINT 受信時に何もしない (シグナル無視) ように設定 (第一引数 '') してみます。

シグナルを無視に設定した例
$ trap '' SIGINT

この状態では、SIGINT を受信しても反応しないため、次の実行例のように割り込みキー (Ctrl + C) が効きません。

$ while true; do sleep 1; echo "Stop me !"; done
Stop me !
Stop me !
^CStop me !                             <--- Ctrl + C 押下しても効かず、コマンド継続
Stop me !
Stop me !
^Z                                      <--- Ctrl + Z 押下で停止 (SIGTSTP 送信)
[1]+  Stopped                 sleep 1

sigaction (C 言語 API)

sigaction() は、シグナルの動作を設定できる API (C 言語) です 7

次のサンプルプログラム (set_sigh.c) では、SIGINT 受信時に handler 関数を実行するように設定してみます。

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

static void handler(int signo)
{
	/* 
	 * 本来ハンドラ内では、非同期シグナルセーフな関数を使用するべきですが、
	 * ここでは、そうでない printf()、exit() などの関数を使用しています。
	 * 非同期シグナルセーフについては $ man 7 signal をご参照ください。
	 */
	printf(" ... Caught the SIGINT (%d)\n", signo);
	exit(EXIT_SUCCESS);
}

int main(void)
{
	unsigned int sec = 1;
	struct sigaction act;

	// SIGINT 受信時に handler() を実行するように設定。
	memset(&act, 0, sizeof act);
	act.sa_handler = handler;
	sigaction(SIGINT, &act, NULL);

	// Ctrl + C などで終了されるまで、1 秒ごとにメッセージを出力する。
	for (;;) {
		sleep(sec);
		printf("Stop me !\n");
	}
	return 0;
}

次の実行例では、SIGINT 受信後 handler() によってメッセージ (... Caught the SIGINT (2)) を出力しプログラムを終了しています。

$ gcc -o set_sigh set_sigh.c
$ ./set_sigh 
Stop me !
Stop me !
Stop me !
^C ... Caught the SIGINT (2)         <--- Ctrl + C 押下で handler 関数が実行される

なお、その他の API として signal() がありますが、こちらは移植性の観点から非推奨となっています (古い API)。特別な理由が無い限りは使用を避けた方が良いと思います 8

6. シグナルブロック

SIGKILL と SIGSTOP 以外のシグナルは、プロセス毎にブロックできます。

たとえば、あるプロセスが SIGINT を受信した場合、通常は標準動作によりプロセスが終了させられますが、SIGINT をブロックしていた場合は、SIGINT を受信しても、シグナル無視のように無反応となります。

シグナル無視との違いは、受信したシグナルを保留するか否かです。シグナル無視の場合はシグナルを保留しませんが、シグナルブロックの場合は保留できます。そのため、ブロックが解除されると、再度シグナルを受信する必要なく処理できます 9

all_signal-sigblock.jpg

シグナルブロックの設定例

シグナルブロックを設定するには sigprocmask() などの API (C 言語) を使用します 10

次のサンプルプログラム (block_sig.c) では、SIGINT をブロックしてから 5 秒後に SIGINT のブロックを解除してみます。

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

int main(void)
{
	unsigned int sec = 5;
	sigset_t set;

	// シグナル集合を空にし、SIGINT 追加
	sigemptyset(&set);
	sigaddset(&set, SIGINT);

	// SIGINT をブロック (保留) する 
	sigprocmask(SIG_BLOCK, &set, NULL);
	printf("Block the SIGINT for %d sec\n", sec);

	sleep(sec);

	// SIGINT のブロックを解除する
	printf("\nPassed %d sec, unblock the SIGINT\n", sec);
	sigprocmask(SIG_UNBLOCK, &set, NULL);

	printf("Done !\n");

	return 0;
}

実行例では SIGINT ブロック後に SIGINT を送信していますが、SIGNINT 受信時の動作が実行されるのはブロック解除後となっているのが分かります。

$ gcc -o block_sig block_sig.c
$ ./block_sig
Block the SIGINT for 5 sec
^C^C^C^C^C^C^C^C^C^C                <--- Ctrl + C 押下しても反応しない
Passed 5 sec, unblock the SIGINT    <--- SIGINT により終了するため、その後の "Done !" が無出力

7. シグナルの状態確認

シグナルブロック (保留)、無視といった状態は /proc/<PID>/status から確認できます。

$ cat /proc/29498/status
Name:	bash
--- snip ---
SigQ:	0/29305                // このプロセスの実 UID 宛にキューイングされたシグナル / キューイングされたシグナルの上限値
SigPnd:	0000000000000000     // 特定プロセス (スレッド) 宛の処理待ちシグナルの数
ShdPnd:	0000000000000000     // スレッドグループ全体宛の処理待ちシグナルの数
SigBlk:	0000000000010000     // ブロックされるシグナル (ビットマスク値)
SigIgn:	0000000000380004     // 無視されるシグナル (ビットマスク値)
SigCgt:	000000004b817efb     // 捕捉待ちのシグナル (ビットマスク値)
--- snip ---

この中で、SigBlk、SigIgn、SigCgt は複数のシグナルをまとめて表現するために シグナルセット というマスク値で管理されているため、読み方が少しややこしいです。たとえば、SigIgn (0000000000380004) の場合は次のような意味になります (16進数を2進数に変換、1 の位置から対応するシグナルを判断)。つまり、PID 29498 の SigIgn (無視されるシグナル) は 3, 20, 21, 22 に対応する 4 つのシグナルという意味になります。

0000000000380004 (hex) -> 1110000000000000000100 (bit)  シグナル名 番号
                          |||                *---------> SIGQUIT (03)
                          ||*--------------------------> SIGTSTP (20)
                          |*---------------------------> SIGTTIN (21)
                          *----------------------------> SIGTTOU (22)

なお、ps s でも同じような情報は確認できます。

$ ps s
  UID   PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
 1000 29498 0000000000000000 0000000000010000 0000000000380004 000000004b817efb Ss   pts/0      0:00 /bin/bash
 1000 29517 0000000000000000 0000000000010000 0000000000380004 000000004b817efb Ss   pts/1      0:00 /bin/bash
 1000 29572 0000000000000000 0000000000000000 0000000000003000 00000001ef804eff S+   pts/0      0:00 vim kernel/signal.c
 1000 29581 0000000000000000 0000000000000000 0000000000000000 0000000000000000 S    pts/1      0:00 sleep 100
 1000 29588 0000000000000000 0000000000000000 0000000000000000 00000001f3d1fef9 R+   pts/1      0:00 ps s

■ カーネル編 (v5.5)

ここからは、カーネル観点での話です。シグナルを送信してからシグナルアクションが処理されるまでの流れを見ていきます。

まずデータ構造を見ていきます。データ構造とは、カーネル実装で登場する構造体のことを指します。シグナルでは多くの構造体が関連しているため、先に把握しておくことで、コードが理解し易くなると思います。

次にカーネル実装 11 を見ていきます。シグナルは次のようにシグナル生成 (1 段階目)、シグナル配送 (2 段階目) という順に段階的な処理構成となっているため、それぞれ見ていきます。

  • シグナル生成 (1 段階目) ... シグナルを送信したことを送信先のプロセスのデータ構造に記録する (シグナルを保留する)
  • シグナル配送 (2 段階目) ... シグナルに応じて、シグナルアクションを実行する

1. データ構造

シグナルは、次のとおり、 task_struct、signal_struct、sighand_struct など多くの構造体が関連しています。

大変混乱しやすいと思いますので、個人的に重要だと思う箇所に関しては、以下のように用途ごとにまとめました。

保留中シグナルを示すフラグ

シグナル生成によって、シグナルを受け取ったプロセスは、シグナル配送によって処理されるまでの間、シグナル保留状態 (シグナル配送待ち) となります。この状態は TIF_SIGPENDING というフラグで示され、スレッドフラグ (flags) に記録されます。

all_signal-thread_flag.jpg

シグナル保留用のキュー (特定プロセス用、スレッドグループ全体用)

受信したシグナルは、sigqueue 構造体によって管理され、関連する幾つかの情報 (※1) を持ちます。それらは保留用のキューに入れられます (sigpending の list につなげられます)。その際、シグナルの送信宛に応じて 2 種類 (特定プロセス用、スレッドグループ全体用) の内のどちらかのキューが使用されます。

また、保留されたシグナルは signal に記録されます。この値は /proc/<PID>/status から確認できます (前述の「シグナルの状態確認」と後述の「おまけ」参照)。

  • 特定プロセス用

    all_signal-sig_pending.jpg

  • スレッドグループ全体用

    all_signal-sig_share_pending.jpg

(※1) 幾つかの情報とは、次の kernel_siginfo 構造体のメンバを指します。

メンバ 意味
si_signo シグナル番号
si_code シグナルの発生元を示すコード (※2)
si_errno シグナル発生を発生させることになった命令のエラーコード
__sifilelds union のため条件に応じて si_pid (送信先 pid)、si_uid (送信先 uid) などに変化する

(※2) シグナルの発生元を示すコードには次のような値が入ります。

include/uapi/asm-generic/siginfo.h より抜粋

定義名 意味
SI_USER 0 kill()、sigsend()、raise() によるシグナル送信
SI_KERNEL 0x80 カーネルによるシグナル送信
SI_QUEUE -1 sigqueue() によるシグナル送信
SI_TIMER -2 POSIX タイマの時間経過によるシグナル送信
SI_MESGQ -3 POSIX メッセージキューの状態変化によるシグナル送信
SI_ASYNCIO -4 非同期 I/O (AIO) 完了によるシグナル送信
SI_SIGIO -5 SIGIO のキューイングによるシグナル送信
SI_TKILL -6 tkill()、tgkill() によるシグナル送信
SI_DETHREAD -7 sent by execve() killing subsidiary threads 12
SI_ASYNCNL -60 getaddrinfo_a() での名前検索完了によるシグナル送信

なお、上記はシグナル共通の値です。特定のシグナルによっては、次のように別の値が入ることもあります 7

シグナル名 si_code に入る値
SIGBUG BUS_*
SIGCHLD CLD_*
SIGFPE FPE_*
SIGILL ILL_*
SIGPOLL/SIGIO POLL_*
SIGSEGV SEGV_*
SIGTRAP TRAP_*
SIGSYS SYS_SECCOMP

シグナルブロック情報

sigprocmask() などによってブロックされたシグナル番号は、ブロック情報 (blocked, readl_blocked13) に記録されます。この値は /proc/<PID>/status から確認できます (前述の「シグナルの状態確認」 と後述の「おまけ」参照)。

all_signal-sig_block.jpg

シグナルアクション関連の情報

sigaction() などでシグナルアクションの設定を変更すると、シグナルハンドラディスクリプタ (sighand_struct) に幾つかの情報 (※) が格納されます。また、シグナルハンドラディスクリプタの action[x] はシグナル番号 (_NSIG) 個の配列になっているため (action[シグナル番号-1])、シグナル毎に設定されます。

all_signal-sig_handle.jpg

(※) 幾つかの情報とは、次の sigaction 構造体のメンバを指します。

メンバ 意味
sa_flags シグナルの使い方を示す SA_* フラグ7
sa_handler SIG_IGN、SIG_DFL、シグナルハンドラへのポインタなど、シグナルアクションの種類
sa_mask ハンドラ実行時にブロックする (受信を禁止する) シグナル

2. 実装

シグナルを送信してからシグナルアクションが処理される (kill -TERM 1234 を実行した際の処理) までの流れを見ていきます。

以降、「シグナル生成」と「シグナル配送」で分けてそれぞれ見ていきます (「...」 の部分は省略箇所です)

シグナル生成

この段階での主な仕事は、送信先プロセスのデータ構造に「シグナルを送信した (シグナル配送待ちがある)」ということを送信先プロセスのデータ構造に記録し、通知することです (標準動作といったシグナルアクションはシグナル配送で処理されます)。

以降で行うコードリーディングの要約として、シグナル生成における処理の全体像を図示します。なお、関数の呼び出しが多いため、コードリーディングでは、この中でも特に重要な関数 (コア関数) である __send_signal() から読み進めていきます。

all_signal-signal generate.jpg

__send_signal()

この関数は、送信されたシグナルを配送するかどうかを決め (prepare_signal())、シグナルを保留用キューに入れます (__sigqueue_alloc(), list_add_tail()) その際、保留シグナルの番号を記録します (sigaddset())。また、配送する場合は、プロセスディスクリプタのスレッドフラグに TIF_SIGPENDING を立て、配送可能なプロセスを起床します (complete_signal())。

詳細は以降をご参照ください。

kernel/signal.c (L1065)

1065 static int __send_signal(int sig, struct kernel_siginfo *info, struct task_struct *t,
1066 			enum pid_type type, bool force)
1067 {
1068 	struct sigpending *pending;
1069 	struct sigqueue *q;
1070 	int override_rlimit;
1071 	int ret = 0, result;
.... 
1076 	if (!prepare_signal(sig, t, force))
1077 		goto ret;

1076 行目の prepare_signal() では、シグナルを配送する必要があるかどうかををチェックします。たとえば、シグナルハンドラ (t->sighand->action[sig - 1].sa.sa_handler) に SIG_IGN が設定されている場合や、標準動作が無視 (Ign) のシグナルの場合は、配送する必要がないので、ret ラベルまで飛びます。シグナルがブロックされていた場合は、シグナルを保留する必要があるため、以降の処理を行います。

また、標準動作が停止 (Stop) のシグナルと SIGCONT に対しては以下の処理も行います。

  • 標準動作が停止 (Stop) のシグナル

    シグナル保留用キュー (※) から SIGCONT を取り除きます。

  • SIGCONT

    シグナル保留用キュー (※) から全ての停止系シグナルを取り除き、wake_up_state() で __TASK_STOPPED 状態のスレッドを起床します (プロセスを再開します)。

    またプロセス (スレッドグループ) が終了処理中 (SIGNAL_STOP_STOPPED) の場合は、処理が完了したことにして、プロセスディスクリプタのフラグ (flags) に SIGNAL_CLD_CONTINUED と SIGNAL_STOP_CONTINUED を立てます。終了処理中でなくとも group_stop_count がカウントされている場合は、既に終了処理が終わったということなので (おそらく)、プロロセスディスクリプタのフラグ (flags) に SIGNAL_CLD_STOPPED と SIGNAL_STOP_CONTINUED を立てます。

(※) 「データ構造」でも説明しましたが、シグナル保留用キューには、「特定プロセス用 (t->pending)」と「スレッドグループ全体用 (t->signal->shared_pending)」の 2 種類があります。ここでは、その両方のキューから該当するシグナルを取り除きます。

1078 
1079 	pending = (type != PIDTYPE_PID) ? &t->signal->shared_pending : &t->pending;

1079 行目では、pending に「特定プロセス用」「スレッドグループ全体用」のどちらのシグナル保留用キューを使用するかを決めています。今回は kill コマンド実行により、type に PIDTYPE_TGID が渡されるため、pending には、スレッドグループ全体用の方が使われます。

1080 	/*
1081 	 * Short-circuit ignored signals and support queuing
1082 	 * exactly one non-rt signal, so that we can get more
1083 	 * detailed information about the cause of the signal.
1084 	 */
1085 	result = TRACE_SIGNAL_ALREADY_PENDING;
1086 	if (legacy_queue(pending, sig))
1087 		goto ret;

1086 行目の legacy_queue() では、sig が「標準シグナル」かつ「既にシグナル保留用キューに存在するなら」 ret ラベルまで飛びます (標準シグナルは複数の同一のシグナルを受信しても 1 つしか受信できない)。

1088 
1089 	result = TRACE_SIGNAL_DELIVERED;
1090 	/*
1091 	 * Skip useless siginfo allocation for SIGKILL and kernel threads.
1092 	 */
1093 	if ((sig == SIGKILL) || (t->flags & PF_KTHREAD))
1094 		goto out_set;

コメントのとおりですが、シグナルが SIGKILL OR 送信先がカーネルスレッド (PF_KTHREAD フラグ) の場合は、out_set ラベルまで飛び、以降のシグナル保留キュー登録処理などを行いません。

1096 	/*
1097 	 * Real-time signals must be queued if sent by sigqueue, or
1098 	 * some other real-time mechanism.  It is implementation
1099 	 * defined whether kill() does so.  We attempt to do so, on
1100 	 * the principle of least surprise, but since kill is not
1101 	 * allowed to fail with EAGAIN when low on memory we just
1102 	 * make sure at least one signal gets delivered and don't
1103 	 * pass on the info struct.
1104 	 */
1105 	if (sig < SIGRTMIN)
1106 		override_rlimit = (is_si_special(info) || info->si_code >= 0);
1107 	else
1108 		override_rlimit = 0;
1109

1105 行目の SIGRTMIN は 32 なので、sig が標準シグナルがどうかで処理が分岐します。

真 のルート (1106 行目) の is_si_special() では、info が SEND_SIG_NOINFO (ユーザモードのプロセスからシグナルが送信された) か、SEND_SIGPRIV (カーネルからシグナルが送信された) の場合は 真 を返します。右の info->si_code >=0 (SI_USER か SI_KERNEL) の場合も 真 を返します。

なお、今回は kill コマンド実行により info->si_code に SI_USER が設定されるため、override_rlimit には 真 が入ります。

1110 	q = __sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit);
1111 	if (q) {
1112 		list_add_tail(&q->list, &pending->list);
1113 		switch ((unsigned long) info) {
1114 		case (unsigned long) SEND_SIG_NOINFO:
1115 			clear_siginfo(&q->info);
1116 			q->info.si_signo = sig;
1117 			q->info.si_errno = 0;
1118 			q->info.si_code = SI_USER;
1119 			q->info.si_pid = task_tgid_nr_ns(current,
1120 							task_active_pid_ns(t));
1121 			rcu_read_lock();
1122 			q->info.si_uid =
1123 				from_kuid_munged(task_cred_xxx(t, user_ns),
1124 						 current_uid());
1125 			rcu_read_unlock();
1126 			break;
1127 		case (unsigned long) SEND_SIG_PRIV:
1128 			clear_siginfo(&q->info);
1129 			q->info.si_signo = sig;
1130 			q->info.si_errno = 0;
1131 			q->info.si_code = SI_KERNEL;
1132 			q->info.si_pid = 0;
1133 			q->info.si_uid = 0;
1134 			break;
1135 		default:
1136 			copy_siginfo(&q->info, info);
1137 			break;
1138 		}
....

1110 行目では __sigqueue_alloc() にて、シグナル管理用の sigqueue 型 q を新しく確保 (アロケーション) するように試みます。この関数の内部処理では、送信先プロセスの所有者 (ユーザ) が保留するシグナルの数が上限を超えないかどうかをチェックし、超えない場合にアロケーションを試みます。ただし、override_rlimit が 真 なら上限チェックをせずにアロケーションを試みます。

1112 行目から 1138 行目はアロケーション成功時の処理です。

1112 行目の list_add_tail() では、シグナル保留用キューにアロケーションした q を登録し (リストへつなぎ)、1113 行目以降の処理では、引数 info に応じて q へシグナルの情報を格納していきます。

1157 out_set:
....
1159 	sigaddset(&pending->signal, sig);
....

シグナル保留用キューのシグナルセットに現在のシグナル番号を追加します (前述の「シグナルの状態確認」と「データ構造」参照)。

1175 	complete_signal(sig, t, type);
1176 ret:
1177 	trace_signal_generate(sig, info, t, type != PIDTYPE_PID, result);
1178 	return ret;
1179 }

1175 行目の complete_signal() については後述します。

1177 行目の trace_signal_generate() は TRACE_EVENT マクロで signal_generate という名前のトレースポイントを作成しているようです。これは、次のようなカーネルトレース情報の出力に利用 (レポート) されています。

kill-5371  [003] 1058202.036613: signal_generate:      sig=15 errno=0 code=0 comm=sleep pid 5359 grp=1  res=0

complete_signal()

この関数は、送信されたシグナルを __send_signal() で配送すると決めた場合に呼び出されます。

そして、この関数でシグナル配送の準備ができているプロセスを探します。見つけたら該当プロセスのスレッドフラグに TIF_SIGPENDING を立て、起床します (通知します)。

詳細は以降をご参照ください。

kernel/signal.c (L984)

 984 static void complete_signal(int sig, struct task_struct *p, enum pid_type type)
 985 {
 986 	struct signal_struct *signal = p->signal;
 987 	struct task_struct *t;
 988 
 989 	/*
 990 	 * Now find a thread we can wake up to take the signal off the queue.
 991 	 *
 992 	 * If the main thread wants the signal, it gets first crack.
 993 	 * Probably the least surprising to the average bear.
 994 	 */
 995 	if (wants_signal(sig, p))
 996 		t = p;
 ...

995 行目の wants_signal() では、次のパターンでシグナル配送可能 (準備ができている) なプロセスを探します。

  • シグナルがブロックされてない (SIGKILL, SIGSTOP はブロック不可) AND

    • プロセスが終了処理中でない (p->flags に PF_ EXITING が無い) AND
      • シグナル (sig) が SIGKILL (プロセスの状態やフラグ有無は見ないで強制する)
  • シグナルがブロックされてない (SIGKILL, SIGSTOP はブロック不可) AND

    • プロセスが終了処理中でない (p->flags に PF_ EXITING が無い) AND 
      • プロセスの状態 (p->state) が一時停止中 (__TASK_STOPPED) か、デバッガなどにより停止中 (__TASK_TRACED) でない AND
        • プロセスが CPU 上で実行中 (カレントプロセス) OR プロセスのスレッドフラグ (p->thread_info->flags) に TIF_SIGPENDING が無い
1021 	/*
1022 	 * Found a killable thread.  If the signal will be fatal,
1023 	 * then start taking the whole group down immediately.
1024 	 */
1025 	if (sig_fatal(p, sig) &&
1026 	    !(signal->flags & SIGNAL_GROUP_EXIT) &&
1027 	    !sigismember(&t->real_blocked, sig) &&
1028 	    (sig == SIGKILL || !p->ptrace)) {

コメントのとおり、Kill 可能なスレッドを探します。次の条件を全て満たす場合、1039 行目から 1042 行目の処理を行います。

  • sig_fatal() が 真 (※) AND
    • スレッドグループが終了中でない (SIGNAL_GROUP_EXIT は「スレッドグループが終了中」というフラグ) AND
      • シグナルがブロックされていない (sigismember() は、t->real_blocked (ブロック情報) に sig が含まれていれば 真) AND
        • シグナルが SIGKILL OR ptrace 関連フラグ (p->ptrace) がない時

(※) sig_fatal() は次の 2 パターンで 真 を返します。

  • シグナルが標準シグナル AND

    • 標準動作が無視 (Ign) / 停止 (Stop) のシグナルでない AND
      • シグナルアクションが SIG_DFL
  • シグナルがリアルタイムシグナル AND

    • シグナルアクションが SIG_DFL
1029 		/*
1030 		 * This signal will be fatal to the whole group.
1031 		 */
1032 		if (!sig_kernel_coredump(sig)) {

sig_kernel_coredump() は、標準動作がコアダンプ (Core) のシグナルでない時 真 を返します。

1033 			/*
1034 			 * Start a group exit and wake everybody up.
1035 			 * This way we don't have other threads
1036 			 * running and doing things after a slower
1037 			 * thread has the fatal signal pending.
1038 			 */
1039 			signal->flags = SIGNAL_GROUP_EXIT;
1040 			signal->group_exit_code = sig;
1041 			signal->group_stop_count = 0;
1042 			t = p;
1043 			do {
1044 				task_clear_jobctl_pending(t, JOBCTL_PENDING_MASK);
1045 				sigaddset(&t->pending.signal, SIGKILL);
1046 				signal_wake_up(t, 1);
1047 			} while_each_thread(p, t);
1048 			return;
1049 		}
1050 	}

1039 行目から 1042 行目までの処理を行った後は、do-while でスレッドグループ内を走査し終わるまで、次に示す処理をループします。

  • 1044 行目の task_clear_jobctl_pending() で、保留中のジョブコントロールフラグ (t->jobctl) をクリア
  • 1045 行目の sigaddset() で、シグナル保留用キューのシグナルセットに SIGKILL (番号) を追加
  • 1046 行目の signal_wake_up() で、スレッドフラグ (t->flags) に TIF_SIGPENDING を立て、TASK_WAKEKILL (SIGKILL などの重要なシグナルを受信可能) か TASK_INTERRUPTIBLE (何らかのイベント待ち) のプロセスを起床 (通知)

ループを抜けると、この関数での処理を終了します。

1052 	/*
1053 	 * The signal is already in the shared-pending queue.
1054 	 * Tell the chosen thread to wake up and dequeue it.
1055 	 */
1056 	signal_wake_up(t, sig == SIGKILL);
1057 	return;
1058 }

ここまで到達できた場合、signal_wake_up() で、スレッドフラグ (t->flags) に TIF_SIGPENDING を立て、TASK_INTERRUPTIBLE 状態のプロセスを起床します (保留シグナルを通知)。また、シグナルが SIGKILL の場合は TASK_WAKEKILL 状態のプロセスも起床します。

以上で、「シグナル生成」の処理は終わりです。続いて、「シグナル配送」を見ていきます。

シグナル配送

この段階での主な仕事は、シグナルハンドラや標準動作などのシグナルアクションを実行することです。ただし、この処理はプロセスが保留シグナルを持つ (前述の「シグナル生成」で説明した TIF_SIGPENDING フラグを持つ) 場合に限ります。

以降で行うコードリーディングの要約として、シグナル配送における処理の全体像を図示します。なお、シグナル配送は一部アーキテクチャに依存するコードがありますが、ここでは x86 (arch/x86 配下) のコードを見ていきます。

all_signal-signal delivery.jpg

exit_to_usermode_loop()

この関数は、カーネルモードからユーザモードへ戻る度 (割り込み/例外ハンドラ、システコール処理後など) に呼び出され、スレッドフラグをチェックします。その際、TIF_SIGPENDING があると、do_signal() を呼びだします (160 行目)。

詳細は以降をご参照ください。

arch/x86/entry/common.c (L136)

136 static void exit_to_usermode_loop(struct pt_regs *regs, u32 cached_flags)
137 {
138 	/*
139 	 * In order to return to user mode, we need to have IRQs off with
140 	 * none of EXIT_TO_USERMODE_LOOP_FLAGS set.  Several of these flags
141 	 * can be set at any time on preemptible kernels if we have IRQs on,
142 	 * so we need to loop.  Disabling preemption wouldn't help: doing the
143 	 * work to clear some of the flags can sleep.
144 	 */
145 	while (true) {
146 		/* We have work to do. */
147 		local_irq_enable();
... 
158 		/* deal with pending signal delivery */
159 		if (cached_flags & _TIF_SIGPENDING)
160 			do_signal(regs);
... 
171 		/* Disable IRQs and retry */
172 		local_irq_disable();
173 
174 		cached_flags = READ_ONCE(current_thread_info()->flags);
175 
176 		if (!(cached_flags & EXIT_TO_USERMODE_LOOP_FLAGS))
177 			break;
178 	}
179 }

do_signal()

この関数は、標準動作の実行 (get_signal()) やシグナルハンドラの実行 (handle_signal()) を行います。また、必要に応じてシステコールの再実行や特定のシステムコールによって一時的に書き換えられたシグナルブロック情報の復元を行います。

詳細は以降をご参照ください。

arch/x86/kernel/signal.c (L806)

806 /*
807  * Note that 'init' is a special process: it doesn't get signals it doesn't
808  * want to handle. Thus you cannot kill init even with a SIGKILL even by
809  * mistake.
810  */
811 void do_signal(struct pt_regs *regs)
812 {
813 	struct ksignal ksig;
814 
815 	if (get_signal(&ksig)) {
816 		/* Whee! Actually deliver the signal.  */
817 		handle_signal(&ksig, regs);
818 		return;
819 	}
820

815 行目の get_signal() では標準動作の実行と保留用キューからのシグナル取り出しを行います。その結果が 真 (取り出せたら) なら handle_signal() を実行しシグナルハンドラの実行を行います。偽 (取り出せなかった) なら以降の処理を行います (get_signal()、handle_signal() は重要な関数なので詳細は後述します)。

821 	/* Did we come from a system call? */
822 	if (syscall_get_nr(current, regs) >= 0) {
823 		/* Restart the system call - no handlers present */
824 		switch (syscall_get_error(current, regs)) {
825 		case -ERESTARTNOHAND:
826 		case -ERESTARTSYS:
827 		case -ERESTARTNOINTR:
828 			regs->ax = regs->orig_ax;
829 			regs->ip -= 2;
830 			break;
831 
832 		case -ERESTART_RESTARTBLOCK:
833 			regs->ax = get_nr_restart_syscall(regs);
834 			regs->ip -= 2;
835 			break;
836 		}
837 	}

ここはシステムコールの再実行に関する処理です (システムコールの処理中にシグナルを受信した場合、エラーを吐いて処理を中断することがあるため、このような処理があります)。

822 行目の syscall_get_nr() は、ユーザモードのレジスタ (regs) からシステムコール番号を取得します。それが 0 以上だったら、824 行目の syscall_get_erro() でエラー番号を取得し、そのエラー番号に応じてカーネルがシステムコールを再実行します。

なお、システムコールの再実行については「Linuxのシステムコール再実行について」という記事に詳しく書かれていました。regs->ip -= 2 の意味など詳細はこちらをご参照ください。

838 
839 	/*
840 	 * If there's no signal to deliver, we just put the saved sigmask
841 	 * back.
842 	 */
843 	restore_saved_sigmask();
844 }

restore_saved_sigmask() では、ppoll、pselect、epoll、sigsuspend などのシステムコールによって一時的に書き換えられたシグナルブロック情報 (task_struct->blocked) を、あらかじめ退避させていたシグナルブロック情報 (task_struct->saved_sigmask) から復元します。

get_signal()

この関数は、シグナル保留用キューからシグナルを取り出し、シグナルに応じて標準動作の実行を行います。

詳細は以降をご参照ください。

kernel/signal.c (L2521)

2521 bool get_signal(struct ksignal *ksig)
2522 {
2523 	struct sighand_struct *sighand = current->sighand;
2524 	struct signal_struct *signal = current->signal;
2525 	int signr;
.... 
2540 relock:
....
2542 	/*
2543 	 * Every stopped thread goes here after wakeup. Check to see if
2544 	 * we should notify the parent, prepare_signal(SIGCONT) encodes
2545 	 * the CLD_ si_code into SIGNAL_CLD_MASK bits.
2546 	 */
2547 	if (unlikely(signal->flags & SIGNAL_CLD_MASK)) {
2548 		int why;
2549 
2550 		if (signal->flags & SIGNAL_CLD_CONTINUED)
2551 			why = CLD_CONTINUED;
2552 		else
2553 			why = CLD_STOPPED;
2554 
2555 		signal->flags &= ~SIGNAL_CLD_MASK;
2556 
2557 		spin_unlock_irq(&sighand->siglock);
2558 
2559 		/*
2560 		 * Notify the parent that we're continuing.  This event is
2561 		 * always per-process and doesn't make whole lot of sense
2562 		 * for ptracers, who shouldn't consume the state via
2563 		 * wait(2) either, but, for backward compatibility, notify
2564 		 * the ptracer of the group leader too unless it's gonna be
2565 		 * a duplicate.
2566 		 */
2567 		read_lock(&tasklist_lock);
2568 		do_notify_parent_cldstop(current, false, why);
2569 
2570 		if (ptrace_reparented(current->group_leader))
2571 			do_notify_parent_cldstop(current->group_leader,
2572 						true, why);
2573 		read_unlock(&tasklist_lock);
2574 
2575 		goto relock;
2576 	}
....

2543 行目のコメントにも少し書かれていますが、2547 行目のフラグ (SIGNAL_CLD_MASK == SIGNAL_CLD_STOPPED or SIGNAL_CLD_CONTINUED )は、シグナル生成 (__send_signal()) で呼び出される prepare_signal() で SIGCONT の処理をするときに設定されるます。このフラグが設定されている場合は、do_notify_parent_cldstop() で親にシグナル (SIGCHLD) を送信します。

2588 	for (;;) {
2589 		struct k_sigaction *ka;
....
2616 		/*
2617 		 * Signals generated by the execution of an instruction
2618 		 * need to be delivered before any other pending signals
2619 		 * so that the instruction pointer in the signal stack
2620 		 * frame points to the faulting instruction.
2621 		 */
2622 		signr = dequeue_synchronous_signal(&ksig->info);
2623 		if (!signr)
2624 			signr = dequeue_signal(current, &current->blocked, &ksig->info);
2625 
2626 		if (!signr)
2627 			break; /* will return 0 */
.... 

2622 行目の dequeue_synchronous_signal() で同期シグナル (SIG[SEGV|BUS|ILL|TRAP|FPE|SYS]) をキューから取り出します。なければ、2624 行目の dequeue_signal() で非同期シグナルをキューから取り出します。それでもなければ、break でループを抜けこの関数での処理を終了します。

2635 		ka = &sighand->action[signr-1];
2636 
2637 		/* Trace actually delivered signals. */
2638 		trace_signal_deliver(signr, &ksig->info, ka);

2638 行目の trace_signal_deliver() は TRACE_EVENT マクロで signal_deliver という名前のトレースポイントを作成するようです。これは、次のようなカーネルトレース情報の出力に利用 (レポート) されています。定義元のコメントを見るに、ここに到達できた時点でシグナルが配送されたことになるようです。

trace-cmd-17636 [001] 607498.455844: signal_deliver:       sig=2 errno=0 code=128 sa_handler=5567b90d1540 sa_flags=1400000

以降は、シグナル無視、シグナルハンドラ、標準動作などのシグナルアクションに関する処理です。ka (2635 行目) の内容により分岐します。

2639 
2640 		if (ka->sa.sa_handler == SIG_IGN) /* Do nothing.  */
2641 			continue;

C 言語 API のsigaction() などでシグナルアクションに無視 (SIG_IGN) を設定していた場合は、何もしません。

2642 		if (ka->sa.sa_handler != SIG_DFL) {
2643 			/* Run the handler.  */
2644 			ksig->ka = *ka;
2645 
2646 			if (ka->sa.sa_flags & SA_ONESHOT)
2647 				ka->sa.sa_handler = SIG_DFL;
2648 
2649 			break; /* will return non-zero "signr" value */
2650 		}

2642 行目は、SIG_DFL でない場合 (つまり、シグナルハンドラを設定していた場合) 、2644 行目で ksig->kaka のアドレスを入れます。これは get_signal() を抜けたあとの handle_signal() でユーザプロス用のレジスタに設定する時に使用します。

2646 行目の SA_ONESHOT (sigaction() で設定可能なフラグ) はシグナルハンドラ実行後にシグナルアクションをデフォルト (SIG_DFL) に戻すという意味です。つまり、シグナルハンドラの実行するのは、初回のシグナル受信時だけです。

最後に break でループを抜け、この関数での処理を終えます。この場合 signr には取り出したシグナル番号が入っているはずなので、後述の handle_signal() が実行されます。

2651 
2652 		/*
2653 		 * Now we are doing the default action for this signal.
2654 		 */
2655 		if (sig_kernel_ignore(signr)) /* Default is nothing. */
2656 			continue;
....

ここからは、標準動作の実行に関する処理です。

2655 行目の sig_kernel_ignore()signr の標準動作が無視 (Ign) なら 真 を返します。その場合、何もしません。

2671 
2672 		if (sig_kernel_stop(signr)) {
2673 			/*
2674 			 * The default action is to stop all threads in
2675 			 * the thread group.  The job control signals
2676 			 * do nothing in an orphaned pgrp, but SIGSTOP
2677 			 * always works.  Note that siglock needs to be
2678 			 * dropped during the call to is_orphaned_pgrp()
2679 			 * because of lock ordering with tasklist_lock.
2680 			 * This allows an intervening SIGCONT to be posted.
2681 			 * We need to check for that and bail out if necessary.
2682 			 */
2683 			if (signr != SIGSTOP) {
2684 				spin_unlock_irq(&sighand->siglock);
2685 
2686 				/* signals can be posted during this window */
2687 
2688 				if (is_current_pgrp_orphaned())
2689 					goto relock;
2690 
2691 				spin_lock_irq(&sighand->siglock);
2692 			}
2693 
2694 			if (likely(do_signal_stop(ksig->info.si_signo))) {
2695 				/* It released the siglock.  */
2696 				goto relock;
2697 			}
2698 
2699 			/*
2700 			 * We didn't actually stop, due to a race
2701 			 * with SIGCONT or something like that.
2702 			 */
2703 			continue;
2704 		}

2672 行目の sig_kernel_stop()signr の標準動作がプロセス一時停止 (Stop) のシグナルなら 真 を返します。真 なら、2694 行目の do_signal_stop() でプロセスディスクリプタのフラグ (flags) に SIGNAL_STOP_STOPPED を立て、プロセスの一時停止 (TASK_STOPPED) を行います。

ただし、シグナルが SIGSTOP 以外かつ、プロセスグループが孤児 (親なし) の 場合は do_signal_stop() を実行しません (一時停止しません)。

2705 
2706 	fatal:
2707 		spin_unlock_irq(&sighand->siglock);
.... 
2711 		/*
2712 		 * Anything else is fatal, maybe with a core dump.
2713 		 */
2714 		current->flags |= PF_SIGNALED;
2715 
2716 		if (sig_kernel_coredump(signr)) {
2717 			if (print_fatal_signals)
2718 				print_fatal_signal(ksig->info.si_signo);
2719 			proc_coredump_connector(current);
2720 			/*
2721 			 * If it was able to dump core, this kills all
2722 			 * other threads in the group and synchronizes with
2723 			 * their demise.  If we lost the race with another
2724 			 * thread getting here, it set group_exit_code
2725 			 * first and our do_group_exit call below will use
2726 			 * that value and ignore the one we pass it.
2727 			 */
2728 			do_coredump(&ksig->info);
2729 		}
....

2716 行目の sig_kernel_coredump()signr の標準動作がコアダンプ (Core) なら 真 を返します。真 なら 2728 行目の do_coredump() でシグナルディスクリプタのフラグ (flags) に SIGNAL_GROUP_COREDUMP を立て、コアダンプ生成処理を行います。

2731 		/*
2732 		 * Death signals, no core dump.
2733 		 */
2734 		do_group_exit(ksig->info.si_signo);
2735 		/* NOTREACHED */
2736 	}

signr の標準動作がプロセス終了 (Term) やコアダンプ (Core) の場合、2724 行目の do_group_exit() でシグナルディスクリプタのフラグ (flags) に SIGNAL_GROUP_EXIT を立て、プロセス (スレッドグループ) の終了を行います。

なお、2736 行目の } は 2588 行目の for (;;) { の対になる部分です。このループは、2627 行目の break か 2649 行目の break で抜けることができます。前者はシグナル保留用キューから取り出すものが無くなった場合で、後者はシグナルアクション (sa_handler) にシグナルハンドラが設定されていた場合です。

2737 	spin_unlock_irq(&sighand->siglock);
2738 
2739 	ksig->sig = signr;
2740 	return ksig->sig > 0;
2741 }

最後にキューから取り出したシグナル番号が 0 より大きいかどうかを判定します。大きい場合はシグナルを取り出せたということで 真 を返し、次の handle_signal() へ進みます。

handle_signal()

この関数は、システムコールの再実行 (必要なら) と、シグナルハンドラ実行用にユーザモード時のスタックフレームを設定します。

arch/x86/kernel/signal.c (L71)

710 static void
711 handle_signal(struct ksignal *ksig, struct pt_regs *regs)
712 {
713 	bool stepping, failed;
714 	struct fpu *fpu = &current->thread.fpu;
715
... 
719 	/* Are we from a system call? */
720 	if (syscall_get_nr(current, regs) >= 0) {
721 		/* If so, check system call restarting.. */
722 		switch (syscall_get_error(current, regs)) {
723 		case -ERESTART_RESTARTBLOCK:
724 		case -ERESTARTNOHAND:
725 			regs->ax = -EINTR;
726 			break;
727 
728 		case -ERESTARTSYS:
729 			if (!(ksig->ka.sa.sa_flags & SA_RESTART)) {
730 				regs->ax = -EINTR;
731 				break;
732 			}
733 		/* fallthrough */
734 		case -ERESTARTNOINTR:
735 			regs->ax = regs->orig_ax;
736 			regs->ip -= 2;
737 			break;
738 		}
739 	}
740
...

do_signal() でも出ましたが14、ここにもシステムコールの再実行に関する処理があります。ただし、カーネルがシステムコールを再実行するケースは次の 2 パターンのみです。

  • エラーが -ERESTARTSYS (728 行目) かつ sa_flags に SA_RESTART (sigaction() で設定可能なフラグ) が立っている場合
  • エラーが 734 行目の -ERESTARTNOINTR (734 行目) だった場合

それ以外の場合、カーネルはシステムコールを再実行せず regs->ax に -EINTR (関数呼び出しが割り込まれたというエラー) を設定します。システムコールを再実行するかどうかは、このエラーからユーザ (ユーザプログラムの条件) が判断します。

750 	failed = (setup_rt_frame(ksig, regs) < 0);
...
751 	if (!failed) {
752 		/*
753 		 * Clear the direction flag as per the ABI for function entry.
754 		 *
755 		 * Clear RF when entering the signal handler, because
756 		 * it might disable possible debug exception from the
757 		 * signal handler.
758 		 *
759 		 * Clear TF for the case when it wasn't set by debugger to
760 		 * avoid the recursive send_sigtrap() in SIGTRAP handler.
761 		 */
762 		regs->flags &= ~(X86_EFLAGS_DF|X86_EFLAGS_RF|X86_EFLAGS_TF);
763 		/*
764 		 * Ensure the signal handler starts with the new fpu state.
765 		 */
766 		fpu__clear(fpu);
767 	}
768 	signal_setup_done(failed, ksig, stepping);
769 }

750 行目の setup_rt_frame() では、シグナルハンドラ実行用にユーザモード時のスタックフレームを設定します。スタックフレームには、シグナルハンドラ実行に必要な情報 (シグナル番号、siginfo 構造体 の情報や rt_sigreturn() システムコールをユーザモードプロセスから呼び出すための情報が格納されています。rt_sigreturn() はシグナルハンドラ実行後にカーネルモードへ復帰するのに必要です。

また、setup_rt_frame() での設定が成功した場合は、762、766 行目でフラグ (regs->flags) や FPU state (浮動小数点数レジスタ) をクリアします (理由は不明)。

そして、最後 (768 行目) の signal_setup_done() では setup_rt_frame() での設定成否で処理が分岐します。

  • 成功した場合:
    sigorsets() でカレントプロセスのブロック情報 (current->blocked) と sigaction() でユーザが設定したブロック情報 (sigaction->sa_mask) の論理和 (OR) を取り、それをカレントプロセスのブロック情報に設定します。また、スレッドフラグに TIF_SINGLESTEP (デバッガで使用) が設定されていた場合は、ptrace_notify(SIGTRAP) を実行します。

  • 失敗した場合:
    force_sigsegv() でカレントプロセスに SIGSEGV を送信します。

以上で、「シグナル配送」の処理は終わりです。

3. おまけ

/proc/<PID>/status にあるシグナル関連のコード

/proc/<PID>/status にある SigPnd、ShdPnd、などの値はどこに由来するのかコード上から探してみました。

task_sig() に答えがありました。たとえば、SigBlk (blocked) には 283 行目で p->blocked を入れています。

fs/proc/array.c#L266 (L266)

266 static inline void task_sig(struct seq_file *m, struct task_struct *p)
267 {
268 	unsigned long flags;
269 	sigset_t pending, shpending, blocked, ignored, caught;
270 	int num_threads = 0;
271 	unsigned int qsize = 0;
272 	unsigned long qlim = 0;
273 
274 	sigemptyset(&pending);
275 	sigemptyset(&shpending);
276 	sigemptyset(&blocked);
277 	sigemptyset(&ignored);
278 	sigemptyset(&caught);
279 
280 	if (lock_task_sighand(p, &flags)) {
281 		pending = p->pending.signal;
282 		shpending = p->signal->shared_pending.signal;
283 		blocked = p->blocked;
284 		collect_sigign_sigcatch(p, &ignored, &caught);
285 		num_threads = get_nr_threads(p);
286 		rcu_read_lock();  /* FIXME: is this correct? */
287 		qsize = atomic_read(&__task_cred(p)->user->sigpending);
288 		rcu_read_unlock();
289 		qlim = task_rlimit(p, RLIMIT_SIGPENDING);
290 		unlock_task_sighand(p, &flags);
291 	}
292 
293 	seq_put_decimal_ull(m, "Threads:\t", num_threads);
294 	seq_put_decimal_ull(m, "\nSigQ:\t", qsize);
295 	seq_put_decimal_ull(m, "/", qlim);
296 
297 	/* render them all */
298 	render_sigset_t(m, "\nSigPnd:\t", &pending);
299 	render_sigset_t(m, "ShdPnd:\t", &shpending);
300 	render_sigset_t(m, "SigBlk:\t", &blocked);
301 	render_sigset_t(m, "SigIgn:\t", &ignored);
302 	render_sigset_t(m, "SigCgt:\t", &caught);
303 }

SIGKILL、SIGSTOP がブロックできない理由

SIGKILL と SIGSTOP がブロックできない理由をコード上から探してみました。

シグナルブロックに使う sigprocmask() 実行時に呼び出される sys_rt_sigprocmask() に答えがありました。3025 行目の sigdelsetmask() で新しく設定したブロック情報 (new_set) から SIGKILL と SIGSTOP を削除しています (つまり、SIGKILL と SIGSTOP をブロックするように設定しても実際は設定されない)。

kernel/signal.c (L3004)

3003 /**
3004  *  sys_rt_sigprocmask - change the list of currently blocked signals
3005  *  @how: whether to add, remove, or set signals
3006  *  @nset: stores pending signals
3007  *  @oset: previous value of signal mask if non-null
3008  *  @sigsetsize: size of sigset_t type
3009  */
3010 SYSCALL_DEFINE4(rt_sigprocmask, int, how, sigset_t __user *, nset,
3011 		sigset_t __user *, oset, size_t, sigsetsize)
3012 {
3013 	sigset_t old_set, new_set;
3014 	int error;
.... 
3022 	if (nset) {
3023 		if (copy_from_user(&new_set, nset, sizeof(sigset_t)))
3024 			return -EFAULT;
3025 		sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP));
3026 
3027 		error = sigprocmask(how, &new_set, NULL);
3028 		if (error)
3029 			return error;
3030 	}
.... 
3037 	return 0;
3038 }

また、シグナルブロックは sigaction() の sa_mask でも設定できますので、こちらも見てみます。

sigaction() の場合は sys_rt_sigaction() -> do_sigaction() の 3967、3968 行目で同様の処理を行っていました。

3949 int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
3950 {
3951 	struct task_struct *p = current, *t;
3952 	struct k_sigaction *k;
3953 	sigset_t mask;
....
3966 	if (act) {
3967 		sigdelsetmask(&act->sa.sa_mask,
3968 			      sigmask(SIGKILL) | sigmask(SIGSTOP));
....
3988 	}
....
3991 	return 0;
3992 }

SIGKILL、SIGSTOP を無視 or シグナルハンドラを設定できない理由

SIGKILL、SIGSTOP が無視 or 動作変更できない理由もコード上から調べてみました。

sigaction() 実行時に呼び出される sys_rt_sigaction() -> do_sigaction() の 3955 行目に答えがありました。一番右の条件で、act が有効値かつ sig_kernel_only() が 真 (シグナルが SIGKILL か SIGSTOP の場合) の時に -EINVAL でエラー (引数が無効) を返しています。つまり、sigaction() に SIGKILL および SIGSTOP を渡すと -EINVAL で失敗します。この場合は当然ですが、無視 (SIG_IGN) もシグナルハンドラ (ユーザ定義の関数) も設定できないということになります。

3949 int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
3950 {
3951 	struct task_struct *p = current, *t;
3952 	struct k_sigaction *k;
3953 	sigset_t mask;
3954 
3955 	if (!valid_signal(sig) || sig < 1 || (act && sig_kernel_only(sig)))
3956 		return -EINVAL;
....
3966 	if (act) {
....
3969 		*k = *act;
3970 		/*
3971 		 * POSIX 3.3.1.3:
3972 		 *  "Setting a signal action to SIG_IGN for a signal that is
3973 		 *   pending shall cause the pending signal to be discarded,
3974 		 *   whether or not it is blocked."
3975 		 *
3976 		 *  "Setting a signal action to SIG_DFL for a signal that is
3977 		 *   pending and whose default action is to ignore the signal
3978 		 *   (for example, SIGCHLD), shall cause the pending signal to
3979 		 *   be discarded, whether or not it is blocked"
3980 		 */
3981 		if (sig_handler_ignored(sig_handler(p, sig), sig)) {
3982 			sigemptyset(&mask);
3983 			sigaddset(&mask, sig);
3984 			flush_sigqueue_mask(&mask, &p->signal->shared_pending);
3985 			for_each_thread(p, t)
3986 				flush_sigqueue_mask(&mask, &t->pending);
3987 		}
3988 	}
....
3991 	return 0;
3992 }

■ 参考文献

  1. 詳細は man 7 signal をご参照ください。 2

  2. 詳細は man 5 core をご参照ください。

  3. 詳細は man 7 pipe ご参照ください。

  4. 詳細は man 2 setrlimit ご参照ください。 2

  5. 詳細は man 2 seccomp ご参照ください。

  6. 詳細は man 1 bash をご参照ください。

  7. 詳細は man 2 sigaction をご参照ください。 2 3

  8. 詳細は man 2 signal をご参照ください。

  9. シグナル無視の場合も解除はできますが、シグナル無視の状態で受信したシグナルは保留されないため、解除された後も処理されません。無視を解除した上で再度シグナルを受信する必要があります。

  10. 詳細は man 2 sigprocmask をご参照ください。

  11. タイトルに記載のとおり、カーネルバージョンは調査時点での最新である v5.5 を使用します。

  12. man 2 execve カーネルソースなどを調べましたが、この原文以上の情報が見当たらず、意味がよく理解できなかったため、原文のまま記載します。

  13. real_blocked は、rt_sigtimedwait(), rt_sigtimedwait_time32(), rt_sigtimedwait_time64() システムコール使用時に設定されるようです。

  14. do_signal() でのシステムコール再実行は、handle_signal() を実行しない場合に通るパス (get_signal() が 偽 の時) なので、ここでのシステムコール再実行処理とは重複しません。

264
274
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
264
274

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?