Linux シグナルについて調査

障害対応でプロセスを終了するためによく利用される「kill」コマンドですが、プロセスが終了するパターンもあれば「kill -9 <PID>」でようやくプロセスが終了してくれるパターンもあります。

違いは「シグナル」ということなのですが、そもそもこの「シグナル」とは一体何なのでしょうか?

シグナルとは?

シグナルとは一言で言うとプロセスに対して何らかのイベントが発生したことを通知するOSの機能です。

 

例えば以下のようなシグナルを送信してイベントをOSに伝えます。

  • ユーザーが [Ctrl] + [C] キーを押下した
  • CPUがゼロ除算を検知した
  • ハードウェアからの割り込みを検知した
  • シグナルはプロセスから送信される
  • プロセスにシグナルを送り割り込みを行ったりする

 

シグナルはそれぞれが「処理方法」(例えば強制終了など)を持っています。

この処理方法によりシグナルが送信された時にプロセスがどのような振舞いをするかが決まります。

 

各プロセスがシグナルを受信した際に以下のいずれか一つの処理をします。

  • デフォルトの動作を実行する
  • シグナルを無視する
  • シグナルハンドラ (signal handler)でシグナルを捕捉する

 

 

シグナルはプロセスを終了(殺す)ためだけのものではない

よく現場では「プロセスをkillできない」「プロセスを殺せない」など「kill」や「殺す」などを慣用的に使っていますが(笑)、「シグナルを送信する」=「プロセスを終了する」ではありません。

言い方を変えると「シグナルはプロセスの状態を変化させるために送信する」と言えます。

後程解説しますが「SIGHUP」は httpd などのデーモンに設定ファイル(コンフィグファイル)を再読み込みさせるために使ったりします。

 

 

シグナルの送り方

シグナルは「kill」コマンドで送信することができます。

killコマンドの引数により、「指定されたプロセス」や「指定されたプロセスグループの全メンバー」などシグナルを送ることができます。

 

シグナルは「ブロック」することも「無視」することもできますが、SIGKILL と SIGSTOP だけはキャッチ、ブロック、無視できません。

主要なシグナル一覧

シグナル    値   意 味

HUP        1        制御している端末のプロセスがハングアップした

INT           2    キーボードからの割り込み命令( [Ctrl]+[c] )

QUIT     3   キーボードからの中止命令( [Ctrl]+[\] )

TERM    15   プロセスの終了命令(デフォルト)

KILL    9   プロセスの強制終了命令

STOP     19   プロセスの停止命令(団到+図)

CONT       18        プロセスの再開命令

 

よく使うのはデフォルトの TERM か INT です。

例えばプロセスがスクリプトを作成して実行したが、不具合で終了しない場合などで Teraterm 上より [Ctrl]+[c] でシェルを終了させたり、Teraterm が実行端末ではない場合(バックグラウンド等で動いている場合)は kill コマンドで終了させます。

 

kill コマンドのデフォルトは SIGTERM

SIGTERMは、kill コマンドがデフォルトで送信する終了シグナルです。

このシグナルはアプリケーションから捕捉できるので、アプリケーションが(プロセスが)終了前の後処理を実施する猶予を与えられます。

つまり、いきなり電源コードを引っこ抜かれるといったシグナルではなく、シャットダウンコマンドのように終了処理をすることができます。

 

Ctrl + C は SIGINT のシグナルを送っている

SIGINT は 「Ctrl」+「C」キーを押下した際に送られるシグナルです。

このシグナルが送信されると、プロセスは現在の処理を中断することを求められます。

通常のプログラムはそのまま終了します。

 

kill コマンドで指定できるシグナル一覧

「kill -l」コマンドでどのシグナルがどの番号かが分かります。

例えば

  • SIGHUP → kill -1 <PID> or kill -SIGHUP <PID>
  • SIGKILL → kill -9 <PID> or kill -SIGKILL <PID>
  • SIGTERM → kill -14 <PID> or kill -SIGTERM <PID>

であることが分かります。

[root@cnt07 include]# 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
[root@cnt07 include]#

シグナルにはレベルがある

シグナルには「レベル」があります。

 

通常終了から強制終了まで様々なレベルがあります。

 

kill -TERM <PID>(または kill -15 <PID>)

↓プロセスが終了しない場合は

kill -SIGHUP <PID>(または kill -1 <PID>)

↓それでもプロセスが終了しない場合

kill -KILL <PID>(または kill -9 <PID>)

SIGHUPとは?

プロセスに「SIGHUP」のシグナルを送信した際のプロセスの動作は決まっていません。

httpd などのデーモンプロセスに対して設定ファイルを再読み込みさせるために利用したりします。

httpdは終了しません。そのため Web サーバーとしてのサービスは継続できます。

 

[root@cnt07 log]# ps -ef | grep httpd
root 28615 1 0 17:22 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
apache 28807 28615 0 17:26 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
apache 28808 28615 0 17:26 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
apache 28809 28615 0 17:26 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
apache 28810 28615 0 17:26 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
apache 28811 28615 0 17:26 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
root 28839 10556 0 17:27 pts/0 00:00:00 grep –color=auto httpd
[root@cnt07 log]# kill -SIGHUP 28615
[root@cnt07 log]#

 

 

SIGHUPの歴史

もともとコンピュータに接続するためには、「シリアルケーブル」をコンピュータに接続して「RS-232」プロトコルを利用していました。

SIGHUPはコンピューターを利用しているユーザーがモデムを切断して接続を終了した場合に、コンピューターのプログラムに送信されます。

 

また、昔は以下の「POSIX.1-1990」に定義されているシグナルはありませんでした。

 

シグナル        値        動作    コメント

SIGUSR1   30,10,16   Term   ユーザ定義シグナル 1

SIGUSR2   31,12,17   Term   ユーザ定義シグナル 2

 

そのため、デーモンなど端末を必要としないプログラムは SIGHUP を設定ファイルの再読み込みのシグナルとして利用しました。

 

つまり SIGHUP は歴史的に以下の2点で利用されています。

  • 端末(Teratermなど仮想端末)が閉じられた場合
  • 設定ファイルを再読み込みしたい場合

 

また、シェルの場合は、親プロセスが SIGHUP シグナルを受信した場合、子プロセスも終了させます。

 

ただし、子プロセスの終了を回避することもできます。

nohup コマンドで実行されている場合は SIGHUP シグナルをブロックします(無視します)。

なぜゾンビプロセスができるのか?

プロセスが終了することを「プロセスが死ぬ」と表現することがあります。

プロセスは死ぬとその姿を消しますが、何らかの原因でプロセスの情報(プロセステーブル)が残ってしまうことがあります。

これが「ゾンビプロセス」です。

 

ps コマンドでゾンビを見た場合

ps コマンドでは「ps aux」コマンドを実行した際に表示される「STAT」で「Z」の値で表示されます。

man ps コマンドでは「Z    終了したが、親プロセスによって回収されなかった、 消滅した (ゾンビ) プロセス」との解説されています。

「ps -ef」コマンドでは「<defunct>」として表示されます。

defunctの意味は「故人となった、消滅した、現存しない」という意味です。

 

ゾンビプロセスとは、プロセスの実態は存在していないけど管理情報(プロセステーブル)だけが残っている状態です。

 

プロセスが終了する場合の処理

プロセスが終了する場合の処理ですが、プロセスが終了する直前に親プロセスに知らせます。

親プロセスがシグナルを受信すると PID がプロセステーブルから削除されます。

親プロセスがすでに存在しなかった場合は「systemd もしくは init」が介入しますが、ほとんど失敗してゾンビプロセスとして残ります。

 

 

プロセスは forkで生成されると「プロセステーブル」に登録されて管理されます。

プロセステーブルには

  • プロセスID 
  • 実行時間
  • 実行ユーザー
  • プログラム名

などが記録されて管理されています。

 

ゾンビプロセスは「子プロセス」が終了する前に「親プロセス」が終了してしまい、子プロセスが終了しても誰も引き取り手がいない状態のことを言います。

ちなみに、すべてのプロセスは終了した後は「ゾンビプロセス」の状態になります。

 

 

プロセステーブルとは?

プロセステーブル(process table)はカーネルのメモリ上に存在します。

Linuxの場合は「/proc」ファイルシステムがあり、psコマンドなどのプログラムから「プロセステーブル」を見えます。

/procは、ファイルアクセスのシステムコール経由でプロセスを確認したり操作できるようになっています。

 

 

ゾンビプロセスの終了方法(殺し方)

ゾンビプロセスを終了させるためには「kill -9 <PID>」コマンドを実行しますが、ゾンビプロセスの親プロセスが CentOS7 の場合「systemd」CentOS6 の場合「init」の場合は OS 再起動しかありません。

 

また、kill -9 <PID>コマンドでもゾンビプロセスが消えない場合は、やはり OS 再起動しかありません。

ゾンビプロセスはシステムリソースを消費するか?

ゾンビプロセスを発生させないことが一番いいことであるのは明白ですが、仮に「ゾンビプロセス」が発生した場合は、すぐに OS 再起動が必要なのでしょうか?

そもそもプログラムに不具合があるので、すぐに改修する必要があることは当たり前ですが、視点をコンピューターの(サーバーの)リソースに絞って考えてみると、それほどリソースは消費しません。

ゾンビプロセスは、メモリ上にプロセステーブルが残っている状態なので、多少でもメモリのリソースを消費していますが、そのプロセステーブルはプロセスの状態を保持しているテーブルなのでサイズとしては問題視することはないです。

ただし、ゾンビプロセスが次々に発生して、プロセステーブルが一杯になった場合は新規でプロセスを生成することができなくなります。(forkが失敗します)

 

 

 man signal の結果

最後に「man signal」の結果です。

SIGNAL(7) Linux Programmer’s Manual SIGNAL(7)

名前
signal – シグナルの概要

 

説明
Linux は POSIX 信頼シグナル (reliable signal; 以後 “標準シグナル”と表記) と POSIX リアルタイムシグナルの両方に対応している。

 

シグナル処理方法
シグナルはそれぞれ現在の「処理方法 (disposition)」を保持しており、 この処理方法によりシグナルが配送された際にプロセスが どのような振舞いをするかが決まる。

 

後述の表の “動作” の欄のエントリは各シグナルのデフォルトの 処理方法を示しており、以下のような意味を持つ。

—————————–

Term  デフォルトの動作はプロセス終了。

Ign   デフォルトの動作はこのシグナルの無視。

Core  デフォルトの動作はプロセス終了とコアダンプ出力 (core(5) 参照)。

Stop  デフォルトの動作はプロセスの一時停止。

Cont  デフォルトの動作は、プロセスが停止中の場合にその実行の再開。

—————————–

 

プロセスは、 sigaction(2) や signal(2) を使って、シグナルの処理方法を変更することができる (signal(2) の方がシグナルハンドラを設定する際の移植性が低い; 詳細は signal(2) を参照)。

シグナルの配送時に起こる動作としてプロセスが選択できるのは、次のいずれか一つである。

デフォルトの動作を実行する、シグナルを無視する、 シグナルハンドラ (signal handler) でシグナルを捕捉する。

 

シグナルハンドラとは、シグナル配送時に自動的に起動されるプログラマ定義の関数である。 (デフォルトでは、シグナルハンドラは通常のプロセスのスタック上で起動される。 シグナルハンドラが代替スタック (alternate stack) を使用するように設定することもできる。代替スタックを使用するように設定する方法と、どのような際に 代替スタックが役に立つかについての議論については sigaltstack(2) を参照のこと。)

 

シグナルの処理方法はプロセス単位の属性である。

マルチスレッドのアプリケーションでは、あるシグナルの処理方法は 全てのスレッドで同じである。

 

fork(2) 経由で作成された子プロセスは、親プロセスのシグナルの処理方法の コピーを継承する。

execve(2) の前後で、ハンドラが設定されているシグナルの処理方法はデフォルトにリセットされ、無視が設定されているシグナルの処理方法は変更されずそのままとなる。

 

———————————–

シグナルの送信
以下のシステムコールとライブラリ関数を使って、 呼び出し者はシグナルを送信することができる。

 

raise(3)     呼び出したスレッドにシグナルを送る。

kill(2)      指定されたプロセスや、指定されたプロセスグループの全メンバー、システムの全プロセスにシグナルを送る。

killpg(2)     指定されたプロセスグループの全メンバーにシグナルを送る。

pthread_kill(3)  呼び出し者と同じプロセス内の指定された POSIX スレッドにシグナルを送る。

tgkill(2)     指定されたプロセス内の指定されたスレッドにシグナルを送る (このシステムコールを使って pthread_kill(3) は実装されている)。

sigqueue(3)    指定されたプロセスに付属データとともにリアルタイムシグナルを送る。

 

———————————–

シグナルが捕捉されるのを待つ
以下のシステムコールを使って、シグナルが捕捉されるまで 呼び出したプロセスやスレッドの実行を中断 (suspend) することができる (ハンドラが設定されていないシグナルによりそのプロセスが終了した 場合にも実行の停止は終了する)。

 

pause(2)    何かシグナルが捕捉されるまで実行を停止する。

sigsuspend(2)  一時的にシグナルマスク (下記参照) を変更し、 マスクされていないシグナルのいずれかが捕捉されるまで 実行を中断する。

 

———————————–

シグナルの同期受信

シグナルハンドラ経由でシグナルを非同期 (asynchronously) で捕捉する以外にも、シグナルを同期 (synchronously) して受け付けることもできる。 同期して受け付けるとは、シグナルが配送されるまで実行を停止 (block)するということである。

シグナルを受け付けた際に、カーネルは そのシグナルに関する情報を呼び出し者に返す。

 

これを行う一般的な方法が二つある。

 

* sigwaitinfo(2), sigtimedwait(2), sigwait(3) は、指定されたシグナル集合のシグナルの一つが配送されるまで実行を中断する。

どのシステムコールや関数でも、配送されたシグナルに関する情報が返される。

* signalfd(2) が返すファイルディスクリプタを使うと、呼び出し元に配送された シグナルに関する情報を読み出すことができる。

このファイルディスクリプタからの read(2) は、 signalfd(2)の呼び出し時に指定されたシグナル集合のシグナルの一つが呼び出し元に 配送されるまで停止 (block) する。 read(2) が返すバッファにはシグナルに関する情報を格納した構造体が入っている。

 

 

———————————–

シグナルマスクと処理待ちシグナル

シグナルは ブロック (block) されることがある。

ブロックされると、そのシグナルは その後ブロックを解除されるまで配送されなくなる。

シグナルが生成されてから配送されるまでの間、そのシグナルは 処理待ち (pending) であると呼ばれる。

 

プロセス内の各スレッドは、それぞれ独立な シグナルマスク (signal mask) を持つ。

シグナルマスクはそのスレッドが現在ブロックしている シグナル集合を示すものである。

スレッドは、 pthread_sigmask(3)を使って自分のシグナルマスクを操作できる。

伝統的なシングルスレッドのアプリケーションでは、 sigprocmask(2) を使って、シグナルマスクを操作できる。

 

fork(2) 経由で作成された子プロセスは親プロセスのシグナルマスクのコピーを継承する。 execve(2) の前後でシグナルマスクは保持される。

 

生成されるシグナル (したがって処理待ちとなるシグナル) には、プロセス全体宛てと特定のスレッド宛てがある。

例えば、プロセス全体宛てのシグナルは kill(2) を使って送信される。

特定のマシン語の命令の実行の結果として生成される、SIGSEGV や SIGFPE などのシグナルは、スレッド宛てとなる。

また、 pthread_kill(3) を使って特定のスレッド宛てに生成されたシグナルも スレッド宛てとなる。

 

プロセス宛てのシグナルは、そのシグナルをブロックしていないスレッドのうちいずれかの一つに配送することができる。

そのシグナルをブロックしていない スレッドが複数ある場合、シグナルを配送するスレッドはカーネルが 無作為に選択する。

 

スレッドは、 sigpending(2) を使って、現在処理待ちのシグナル集合を取得することができる。

この集合は、プロセス宛ての処理待ちシグナルと 呼び出したスレッド宛てのシグナルの両方から構成される。

 

fork(2) 経由で作成された子プロセスでは、処理待ちのシグナル集合は空の集合で初期化される。 execve(2) の前後で、処理待ちのシグナル集合は保持される。

 

 

標準シグナル
Linux は以下に示す標準シグナルに対応している。

シグナル番号の一部はアー キテクチャ依存であり、”値” 欄に示す通りである。

(3つの値が書かれている ものは、 1つ目が alpha と sparc で通常有効な値、 真ん中が x86, arm や他のほとんどのアーキテクチャでの有効な値、最後が mips での値である。 (parisc での値は記載されていない。 parisc でのシグナル番号は Linux カーネルソースを参照してほしい)。 – はそのアーキテクチャにおいて対応するシグナルがないことを示す。)

 

最初に、POSIX.1-1990 に定義されているシグナルを示す。

 

 

シグナル   値   動作  コメント
———————————————————
SIGHUP     1   Term  制御端末(controlling terminal)のハングアップ検出、または制御しているプロセスの死
SIGINT     2   Term  キーボードからの割り込み (Interrupt)
SIGQUIT    3   Core  キーボードによる中止 (Quit)
SIGILL     4   Core  不正な命令
SIGABRT    6   Core  abort(3) からの中断 (Abort) シグナル
SIGFPE     8   Core  浮動小数点例外
SIGKILL    9   Term  Kill シグナル
SIGSEGV    11   Core  不正なメモリ参照
SIGPIPE     13   Term  パイプ破壊:読み手の無いパイプへの書き出し
SIGALRM    14   Term  alarm(2) からのタイマーシグナル
SIGTERM    15   Term  終了 (termination) シグナル
SIGUSR1  30,10,16  Term  ユーザ定義シグナル 1
SIGUSR2  31,12,17  Term  ユーザ定義シグナル 2
SIGCHLD  20,17,18  Ign   子プロセスの一時停止 (stop) または終了
SIGCONT  19,18,25  Cont  一時停止 (stop) からの再開
SIGSTOP  17,19,23  Stop  プロセスの一時停止 (stop)
SIGTSTP  18,20,24  Stop  端末より入力された一時停止 (stop)
SIGTTIN  21,21,26  Stop  バックグランドプロセスの端末入力
SIGTTOU  22,22,27  Stop  バックグランドプロセスの端末出力

 

シグナル SIGKILL と SIGSTOP はキャッチ、ブロック、無視できない。

 

次に、 POSIX.1-1990 標準にはないが、 SUSv2 と POSIX.1-2001 に記述されているシグナルを示す。

 

シグナル   値   動作  コメント
———————————————————
SIGHUP     1   Term  制御端末(controlling terminal)のハングアップ検出、または制御しているプロセスの死
SIGINT     2   Term  キーボードからの割り込み (Interrupt)
SIGQUIT    3   Core  キーボードによる中止 (Quit)
SIGILL     4   Core  不正な命令
SIGABRT    6   Core  abort(3) からの中断 (Abort) シグナル
SIGFPE     8   Core  浮動小数点例外
SIGKILL    9   Term  Kill シグナル
SIGSEGV    11   Core  不正なメモリ参照
SIGPIPE    13   Term  パイプ破壊:読み手の無いパイプへの書き出し
SIGALRM    14   Term  alarm(2) からのタイマーシグナル
SIGTERM    15   Term  終了 (termination) シグナル
SIGUSR1  30,10,16  Term  ユーザ定義シグナル 1
SIGUSR2  31,12,17  Term  ユーザ定義シグナル 2
SIGCHLD  20,17,18  Ign   子プロセスの一時停止 (stop) または終了
SIGCONT  19,18,25  Cont  一時停止 (stop) からの再開
SIGSTOP  17,19,23  Stop  プロセスの一時停止 (stop)
SIGTSTP  18,20,24  Stop  端末より入力された一時停止 (stop)
SIGTTIN  21,21,26  Stop  バックグランドプロセスの端末入力
SIGTTOU  22,22,27  Stop  バックグランドプロセスの端末出力

 

シグナル SIGKILL と SIGSTOP はキャッチ、ブロック、無視できない。

 

次に、 POSIX.1-1990 標準にはないが、 SUSv2 と POSIX.1-2001 に記述されているシグナルを示す。

 

シグナル 値 動作 コメント
———————————————————
SIGBUS   10,7,10  Core  バスエラー (不正なメモリアクセス)
SIGPOLL        Term  ポーリング可能なイベント (Sys V)。SIGIO と同義
SIGPROF  27,27,29  Term  profiling タイマの時間切れ
SIGSYS   12,31,12  Core  ルーチンへの引き数が不正 (SVr4)
SIGTRAP    5    Core  トレース/ブレークポイント トラップ
SIGURG   16,23,21  Ign   ソケットの緊急事態 (urgent condition) (4.2BSD)
SIGVTALRM 26,26,28  Term  仮想アラームクロック (4.2BSD)
SIGXCPU  24,24,30  Core  CPU時間制限超過 (4.2BSD)
SIGXFSZ  25,25,31  Core  ファイルサイズ制限の超過 (4.2BSD)

 

 

Linux 2.2 以前では、 SIGSYS, SIGXCPU, SIGXFSZ および SPARC と MIPS 以外のアーキテクチャでの SIGBUS のデフォルトの振る舞いは (コアダンプ出力なしの) プロセス終了であった。

(他の UNIX システムにも SIGXCPU と SIGXFSZ のデフォルトの動作がコアダンプなしのプロセス終了のものがある。) Linux 2.4 では、POSIX.1-2001 での要求仕様に準拠して、 これらのシグナルで、プロセスを終了させ、コアダンプを出力する ようになっている。

 

次にその他の各種シグナルを示す。

 

シグナル     値   動作  コメント
———————————————————
SIGIOT      6    Core  IOT トラップ。SIGABRT と同義
SIGEMT    7,-,7    Term
SIGSTKFLT  -,16,-   A    数値演算プロセッサにおけるスタックフォルト (未使用)
SIGIO   23,29,22   Term  入出力が可能になった (4.2BSD)
SIGCLD   -,-,18   Ign   SIGCHLD と同義
SIGPWR   29,30,19   Term  電源喪失 (Power failure) (System V)
SIGINFO   29,-,-       SIGPWR と同義
SIGLOST   -,-,-    Term  ファイルロックが失われた (未使用)
SIGWINCH  28,28,20   Ign   ウィンドウ リサイズ シグナル (4.3BSD, Sun)
SIGUNUSED  -,31,-   Core  SIGSYS と同義

 

(シグナル 29 は alpha では SIGINFO / SIGPWR だが、sparc では SIGLOST である。)

 

SIGEMT は POSIX.1-2001 に規定されていないが、 その他の多くの UNIX システムに存在する。

デフォルトの動作は多くの場合、コアダンプ出力を伴うプロセスの終了である。

SIGPWR は (POSIX.1-2001 に規定されていないが) このシグナルが存在する 他の UNIX システムでは多くの場合、デフォルト動作は無視である。

SIGIO は (POSIX.1-2001 に規定されていないが) いくつかの他の UNIX システムでは デフォルト動作は無視である。

SIGUNUSED が定義されている場合には、ほとんどのアーキテクチャで SIGSYS の同義語となっている。

 

リアルタイムシグナル
Linux はリアルタイムシグナルをサポートしている。

リアルタイムシグナルは元々 POSIX.1b のリアルタイム拡張で定義されて いるものであり、現在では POSIX.1-2001 に含まれている。

対応しているリアルタイムシグナルの範囲は、マクロ SIGRTMIN と SIGRTMAX で定義される。

POSIX.1-2001 では、少なくとも _POSIX_RTSIG_MAX (8) 個のリアルタイムシグナルに対応した実装が要求されている。

 

Linux は、32 個の異なるリアルタイムシグナルに対応しており、 その番号は 33 から 64 である。

しかしながら、glibc の POSIX スレッド実装は、 内部で 2個 (NPTL の場合) か 3個 (LinuxThreads の場合) のリアルタイムシグナルを使用しており (pthreads(7) 参照)、 SIGRTMIN の値を適切に (34 か 35 に) 調整する。

 

利用可能なリアルタイムシグナルの範囲は glibc のスレッド実装により 異なるし (使用するカーネルと glibc により実行時にも変化する)、 UNIX システムの種類によっても異なる。したがって、 プログラムでは「ハードコーディングした数字を使ってのリアルタイムシグナルの 参照は決してすべきではなく」、代わりに SIGRTMIN+n の形で参照すべきである。

また、 SIGRTMIN+n が SIGRTMAX を超えていないかのチェックを (実行時に) 適切に行うべきである。

 

標準シグナルと異なり、リアルタイムシグナルには 事前に定義された意味はない。

リアルタイムシグナルの全部をアプリケーションで定義した用途に使える。

 

ハンドリングしないリアルタイムシグナルのデフォルトの動作は 受信したプロセスの終了である。

 

リアルタイムシグナルは以下の特徴がある:

 

1. リアルタイムシグナルは複数の実体をキューに入れることができる。 一方、標準シグナルの場合、そのシグナルがブロックされている間に 同じシグナルの複数のインスタンスが配送されても、 1 つだけがキューに入れられる。

 

2. シグナルが sigqueue(3) を用いて送信された場合、 付属データ (整数かポインタ) をシグナルと共に送信できる。 受信側プロセスが sigaction(2) に SA_SIGINFO フラグを指定してシグナルハンドラを設定した場合、 このデータは sig‐
info_t 構造体の si_value フィールド経由でハンドラの第 2 引き数として渡され、 利用することができる。 さらに、この構造体の si_pid と si_uid フィールドでシグナルを送信したプロセスの PID と実ユーザ ID を 得ることができる。

 

3. リアルタイムシグナルでは配送される順序が保証される。 同じタイプのリアルタイムシグナルは送信された順番に到着する。 異なるリアルタイムシグナルが一つのプロセスに送信された場合、 番号の小さいシグナルから先に到着する。
(つまり小さい番号のシグナルが高い優先順位を持つ。) 対照的に、一つのプロセスに対して複数の標準シグナルが処理待ちとなった場合、 これらのシグナルが配送される順序は不定である。

 

 

一つのプロセスに対して標準シグナルとリアルタイムシグナルの両方が 処理待ちの場合、POSIX はどちらが先に配送されるかを規定していない。

Linux では、他の多くの実装と同様、このような場合には 標準シグナルが優先される。

 

POSIX によれば、1 プロセス毎に最低 _POSIX_SIGQUEUE_MAX (32) 個のリアルタイムシグナルをキューに入れられるべきとしている。

しかし、 Linux では違った実装になっている。カーネル 2.6.7 までは (2.6.7を含む)、全プロセスでキューに入っているリアルタイムシグナル の数の合計についてシステム全体での制限がある。

この制限は /proc/sys/kernel/rtsig-max ファイルで見ることができ、 (権限があれば) 変更もできる。
関係するファイルとして、 /proc/sys/kernel/rtsig-nr を見ることで、いくつのリアルタイムシグナルが現在キューに入っているかを知ることができる。

Linux 2.6.8 で、これらの /proc 経由のインターフェースは、 RLIMIT_SIGPENDING
リソース制限に置き換えられた。 これは、キューに入るシグナル数に関してユーザ単位に 上限を指定するものである。 詳しくは setrlimit(2) を参照。

 

非同期シグナルで安全な関数 (async-signal-safe functions)
シグナルハンドラ関数には非常に注意しなければならない。 他の場所の処理はプログラム実行の任意の箇所で中断される可能性があるためである。 POSIX には「安全な関数 (safe function)」という概念がある。
シグナルが安全でない関数の実行を中断し、かつ handler が安全でない関数を呼び出した場合、プログラムの挙動は未定義である。

POSIX.1-2004 (POSIX.1-2001 Technical Corrigendum (正誤表) 2 とも言う) では、 シグナルハンドラ内での安全な呼び出しを保証することが必須の関数として 以下が規定されている。

 

_Exit()
_exit()
abort()
accept()
access()
aio_error()
aio_return()
aio_suspend()
alarm()
bind()
cfgetispeed()
cfgetospeed()
cfsetispeed()
cfsetospeed()
chdir()
chmod()
chown()
clock_gettime()
close()
connect()
creat()
dup()
dup2()
execle()
execve()
fchmod()
fchown()
fcntl()
fdatasync()
fork()
fpathconf()
fstat()
fsync()
ftruncate()
getegid()
geteuid()
getgid()
getgroups()
getpeername()
getpgrp()
getpid()
getppid()
getsockname()
getsockopt()
getuid()
kill()

link()
listen()
lseek()
lstat()
mkdir()
mkfifo()
open()
pathconf()
pause()
pipe()
poll()
posix_trace_event()
pselect()
raise()
read()
readlink()
recv()
recvfrom()
recvmsg()
rename()
rmdir()
select()
sem_post()
send()
sendmsg()
sendto()
setgid()
setpgid()
setsid()
setsockopt()
setuid()
shutdown()
sigaction()
sigaddset()
sigdelset()
sigemptyset()
sigfillset()
sigismember()
signal()
sigpause()
sigpending()
sigprocmask()
sigqueue()
sigset()
sigsuspend()
sleep()
sockatmark()
socket()
socketpair()
stat()
symlink()
sysconf()
tcdrain()
tcflow()
tcflush()
tcgetattr()

tcgetpgrp()
tcsendbreak()
tcsetattr()
tcsetpgrp()
time()
timer_getoverrun()
timer_gettime()
timer_settime()
times()
umask()
uname()
unlink()
utime()
wait()
waitpid()
write()

 

POSIX.1-2008 では、上記のリストのうち fpathconf(), pathconf(), sysconf() が削除され、以下の関数が追加された。

 

execl()
execv()
faccessat()
fchmodat()
fchownat()
fexecve()
fstatat()
futimens()
linkat()
mkdirat()
mkfifoat()
mknod()
mknodat()
openat()
readlinkat()
renameat()
symlinkat()
unlinkat()
utimensat()
utimes()

 

シグナルハンドラによるシステムコールやライブラリ関数への割り込み
システムコールやライブラリが停止 (block) している間にシグナルハンドラが 起動されると、以下のどちらかとなる。

 

* シグナルが返った後、呼び出しは自動的に再スタートされる。

* 呼び出しはエラー EINTR で失敗する。

 

これらの二つの挙動のうちどちらが起こるかは、インターフェイスにより依存し、 シグナルハンドラが SA_RESTART フラグ (sigaction(2) 参照) を使って設定されていたかにも依存する。

詳細は UNIX システムによって異なる。

Linux における詳細を以下で説明する。

 

以下のインターフェイスのいずれかの呼び出しが停止している間に シグナルハンドラにより割り込まれた場合、 SA_RESTART フラグが使用されていれば、シグナルハンドラが返った後に その呼び出しは自動的に再スタートされることになる。

それ以外の場合は、その呼び出しはエラー EINTR で失敗することになる。

 

 

* read(2), readv(2), write(2), writev(2), ioctl(2) の「遅い (slow)」デバイスに対する呼び出し。

ここでいう「遅い」デバイスとは、I/O 呼び出しが無期限に停止 (block) する可能性のあるデバイスのことで、例としては端末、パイプ、ソケットがある (この定義では、ディスクは遅いデバイスではない)。

遅いデバイスに対する I/O 呼び出しが、 シグナルハンドラにより割り込まれた時点までに何らかのデータをすでに転送していれば、呼び出しは成功ステータス (通常は、転送されたバイト数) を返すことだろう。

 

* 停止 (block) する可能性のある open(2) (例えば、FIFO のオープン時; fifo(7) 参照)。

* wait(2), wait3(2), wait4(2), waitid(2), waitpid(2).

 

* ソケットインターフェイス: accept(2), connect(2), recv(2), recvfrom(2), recvmsg(2), send(2), sendto(2), sendmsg(2). 但し、ソケットにタイムアウトが設定されていない場合 (下記参照)。

 

* ファイルロック用インターフェイス: flock(2), fcntl(2) F_SETLKW.

 

* POSIX メッセージキューインターフェイス: mq_receive(3), mq_timedreceive(3), mq_send(3), mq_timedsend(3).

 

* futex(2) FUTEX_WAIT (Linux 2.6.22 以降; それ以前は常に EINTR で失敗していた)。

 

* POSIX セマフォインターフェイス: sem_wait(3), sem_timedwait(3) (Linux 2.6.22 以降; それ以前は常に EINTR で失敗していた)。

 

以下のインターフェイスは、 SA_RESTART を使っているどうかに関わらず、シグナルハンドラにより割り込まれた後、 再スタートすることは決してない。

これらは、シグナルハンドラにより割り込まれると、常にエラー EINTR で失敗する。

 

* setsockopt(2) を使ってタイムアウトが設定されているソケットインターフェース: accept(2), recv(2), recvfrom(2), recvmsg(2) で受信タイムアウト (SO_RCVTIMEO) が設定されている場合と、 connect(2), send(2), sendto(2), sendmsg(2) で送信タイムアウト (SO_SNDTIMEO) が設定されている場合。

 

* シグナル待ちに使われるインターフェイス: pause(2), sigsuspend(2), sigtimedwait(2), sigwaitinfo(2).

 

* ファイルディスクリプタ多重インターフェイス: epoll_wait(2), epoll_pwait(2), poll(2), ppoll(2), select(2), pselect(2).

 

* System V IPC インターフェイス: msgrcv(2), msgsnd(2), semop(2), semtimedop(2).

 

* スリープ用のインターフェイス: clock_nanosleep(2), nanosleep(2), usleep(3).

 

* inotify(7) ファイルディスクリプタからの read(2).

 

* io_getevents(2).

 

sleep(3) 関数も、ハンドラにより割り込まれた場合、決して再スタートされることはない。 しかし、成功となり、残っている停止時間を返す。

 

一時停止シグナルによるシステムコールやライブラリ関数への割り込み
Linux では、シグナルハンドラが設定されていない場合でも、 いくつかのブロッキング型のインターフェイスは、 プロセスが一時停止 (stop) シグナルの一つにより停止され、 SIGCONT により再開された後に、エラー EINTR
で失敗する可能性がある。

この挙動は POSIX.1 で認められておらず、他のシステムでは起こらない。

この挙動を示す Linux のインターフェイスは以下の通りである。

* setsockopt(2) を使ってタイムアウトが設定されているソケットインターフェース: accept(2), recv(2), recvfrom(2), recvmsg(2) で受信タイムアウト (SO_RCVTIMEO) が設定されている場合と、 connect(2), send(2), sendto(2),
sendmsg(2) で送信タイムアウト (SO_SNDTIMEO) が設定されている場合。

* epoll_wait(2), epoll_pwait(2).

* semop(2), semtimedop(2).

* sigtimedwait(2), sigwaitinfo(2).

* inotify(7) ファイルディスクリプタからの read(2).

* Linux 2.6.21 以前: futex(2) FUTEX_WAIT, sem_timedwait(3), sem_wait(3).

* Linux 2.6.8 以前: msgrcv(2), msgsnd(2).

* Linux 2.4 以前: nanosleep(2).

 

 

準拠
POSIX.1 (注記した内容以外)。

 

関連項目
kill(1), getrlimit(2), kill(2), killpg(2), rt_sigqueueinfo(2), setitimer(2), setrlimit(2), sgetmask(2), sigaction(2), sigaltstack(2), signal(2), signalfd(2), sigpending(2), sigprocmask(2), sigsuspend(2), sigwaitinfo(2),
abort(3), bsd_signal(3), longjmp(3), raise(3), pthread_sigqueue(3), sigqueue(3), sigset(3), sigsetops(3), sigvec(3), sigwait(3), strsignal(3), sysv_signal(3), core(5), proc(5), pthreads(7), sigevent(7)

 

この文書について
この man ページは Linux man-pages プロジェクトのリリース 3.51 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。

Linux

 

まとめ

うまいことまとめようと思ったのですが、テーマが膨大過ぎてまとまりませんでした。

今自分が業務で必要とされている知識を思いつくままに調べて記事にしたような感じです。

今後時間があればどんどん内容を追加して充実させていこうと思います。

 

 

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

AlphaOmega Captcha Medica  –  What Do You See?
     
 

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください