シグナルと trap コマンド

シグナル

シグナルとは?

シグナルとは、実行中のプロセスに対して、さまざまなイベントを通知するために送出されるものである。

よく使用される、プロセスを終了するためのシグナルである SIGTERM や SIGKILL などのほかにも、数十種類のシグナルが存在する。

使用可能なシグナルの一覧は、コマンドラインから kill -l を実行することで参照可能である。

$ 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     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      35) SIGRTMIN    36) SIGRTMIN+1
37) SIGRTMIN+2  38) SIGRTMIN+3  39) SIGRTMIN+4  40) SIGRTMIN+5
41) SIGRTMIN+6  42) SIGRTMIN+7  43) SIGRTMIN+8  44) SIGRTMIN+9
45) SIGRTMIN+10 46) SIGRTMIN+11 47) SIGRTMIN+12 48) SIGRTMIN+13
49) SIGRTMIN+14 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

シグナルを送出する

kill [シグナル番号|シグナル名] PID

→ プロセスにシグナルを送出するには kill コマンドを使用する。

シグナルを送出するには、 kill コマンドに送出先プロセスのプロセス ID と、送出するシグナルを指定して実行する。

シグナルの指定には、各シグナルに定義されているシグナル番号かシグナル名のどちらかを使用する。送出先プロセスの プロセス ID (PID) は、ps コマンドや pgrep コマンドで確認することができる。

シグナル番号・シグナル名を省略した場合は、デフォルトで15番シグナル(TERM)が送出される。

$ sleep 100000 &
[1] 5355
#↑sleep コマンドに & を付けてバックグラウンドで実行する。

$ pgrep -lf sleep
5355 sleep 100000
#↑pgrep コマンドでプロセス ID を検索する。

$ kill -15 5355
#↑TERM シグナル送出。

$ pgrep -lf sleep
[1]+  終了しました            sleep 100000
#↑再検索すると終了していることを確認できる。

各シグナルの意味

kill コマンドでプロセスに対してシグナルを送出すると、割り込みや強制終了など各シグナルの意味に沿った処理が実行される。

各シグナルが持っている意味は、以下の表のとおりである。

一般的なシグナル番号一覧

シグナル番号 シグナルが持つ意味
0 プロセス終了時に、プロセスが自分自身に対して送出する EXIT シグナル。
1 XWindow のクローズや、デーモンのリセットに使用されるハングアップシグナル。
2 Ctrl+C や Del キーを押したときに発生する割り込みシグナル。
3 Ctrl+\ を押したときに発生するクイットシグナル。
9 プロセスを強制終了するためのキルシグナル。強制終了なので当然、trap はできない。
15 プロセスを終了させるための終了シグナル。kill コマンドはデフォルトでこのシグナルを使用する (つまり kill PIDkill -15 PID と同じ結果になる)。

一般的なシグナル名一覧

シグナル番号 シグナル名 通知内容
1 HUP プロセスに再起動を通知する。
2 INT プロセスに割り込みを通知する。(Ctrl+c)
3 QUIT プロセスに終了を通知する。(coreを作成する)
9 KILL プロセスに強制終了を通知する。
15 TERM プロセスに終了を通知する。(デフォルト)
18 CONT プロセスに再開を通知する。
19 STOP プロセスに中断を通知する。
20 TSTP プロセスにサスペンドを通知する。(Ctrl+Z)

trap コマンド

trap コマンドの使用方法

trap コマンドは送出されたシグナルを捕捉し、あらかじめ指定されていた処理を実行するコマンドである。

trap 'コマンド' シグナルリスト

→ シェルスクリプトに対して送出されたシグナルは trap コマンドで捕捉できる。

実行中のシェルスクリプトに対して送出されたシグナルは、trap コマンドを使用することで捕捉することが可能である。

kill コマンドなどによりシグナルリストに指定されたシグナルが送出されると、trap コマンドはそれを捕捉し、指定したコマンドを実行する。trap コマンドを使用することにより、各シグナルの規定の動作を置き換えることができる。

ただし、強制終了のシグナルである9番は trap することはできないので注意が必要だ。

$ trap 'echo trapped.' 2
※ ここで Ctrl+c を押す。
$ trapped.

trap 処理をリセットする

trap コマンドでシグナル捕捉時に実行するように指定した動作は、リセットを行うことで解除することができる。

trap シグナルリスト

→ trap コマンドにシグナルリストのみを指定し、指定されたシグナルの捕捉処理をリセットする。

trap コマンドで指定した処理をリセットしたい場合、つまり trap 'rm -f *.tmp; exit 1' 1 2 3 15 などと指定していた動作を、各シグナルのデフォルトの動作に戻したい場合は、trap 1 2 3 15 のように trap コマンドにリセットしたいシグナルのみを指定し実行する。

trap コマンドの処理を指定していた部分に何も書かないことにより、シグナル受信時にデフォルトの動作をするようになる。ただし、処理部分に何も書かないと言っても**「trap ’’ 1 2 3 15」とはまったく意味が違う**ので注意が必要だ。

trap '' 1 2 3 15 と書くと、シグナルを捕捉した場合は '' を実行する、つまり何もしない処理を実行するという意味になり、結果として該当するシグナルを無視するという設定になってしまう。

$ trap 'echo trapped.' 2
※ ここで Ctrl+c を押す。
$ trapped.
#↑trap に指定した処理が実行されている。

$ trap 2
#↑trap をリセットする。
※ ここで Ctrl+c を押す。
$
#↑指定した処理が解除されているのが確認できる。

trap コマンドの応用 1

trap コマンドをシェルスクリプトに組み込むことによって、シグナル受信により実行途中で終了する場合に行う、終了処理を指定することが可能になる。

trap 'command' 1 2 3 15

→ 通常の用途では trap するシグナルは 1, 2 , 3, 15 のみでよい。

シェルスクリプト内で受け取る可能性があるシグナルには 9番(SIGKILL)もあるが、これは強制終了に使用されるシグナルなので、指定しても trap することはできない。

したがって、シェルスクリプト内で trap 処理を行う場合は、1、2、3、15 のみを指定する。

実際に trap コマンドを使用し、終了前にゴミ掃除を行うシェルスクリプト (trap_rm.sh) を作成してみる。

実行中のシェルスクリプトを割り込みなどにより途中で終了した場合、スクリプト中で作成した一時ファイルが残ってしまうことがある。このようなゴミファイルをなるべく残さないようにするために、trap コマンドを使用し終了前にゴミ掃除を行う処理を追加する。

#!/bin/bash

# ゴミ掃除用の trap 処理を指定する。
trap 'echo "trapped."; rm -f temp.$$; exit 1' 1 2 3 15

# trap時に削除するファイルを作成する。
echo "Trap Test." >temp.$$
echo "作成されたファイルを確認する。"
ls -l temp.$$

# Ctrl+cが押されるまで無限ループ。
echo "Ctrl+cを押してください。"
while :
do
  :
done

このシェルスクリプト trap_rm.sh の実行結果は、以下のとおりとなる。

$ ./trap_rm.sh
作成されたファイルを確認する。
-rw-r--r-- 1 sunone sunone 11  6月 2日 14:01 temp.9535
Ctrl+cを押してください。
※ ここで Ctrl+c を押す。
trapped.
#↑トラップ処理が実行されている。

$ ls temp.9535
/bin/ls: temp.9535: そのようなファイルやディレクトリはありません
#↑トラップ処理によりファイルが削除されていることが確認できる。

trap コマンドの応用 2

EXIT シグナルを trap して、シェルスクリプト終了時に必ず実行される処理を指定する。

trap 'command' EXIT

→ シェルスクリプト終了時に送信される 0番のシグナル (EXIT) を trap する。

シェルスクリプトが終了するときに終了メッセージを表示するといったような処理は、あらかじめ EXIT シグナルを trap しておくことで簡単に実現できる。

また、trap 'command' 0 といったように0番を指定しても結果は同じになるが、この trap 処理が終了処理であることが明確に分かる EXIT を指定するようにした方がよい。

実際に終了処理を行うシェルスクリプト (trap_exit.sh) を作成してみる。

#!/bin/bash

# EXITシグナルをtrapして終了メッセージを指定する。
trap "echo '`basename $0`を終了します.'" EXIT

# 他のシグナルもtrapしておく。
trap "echo '他のシグナルをtrapしました。'" 1 2 3 15

# Ctrl+Cで終了するテストのためにsleepしておく。
sleep 10

exit 0

このシェルスクリプト trap_exit.sh の実行結果は、以下のとおりとなる。

$ ./trap_exit.sh
trap_exit.shを終了します.
#↑終了時に EXIT シグナルを trap したメッセージが表示されている。

$ ./trap_exit.sh
※ ここで Ctrl+c を押す。
他のシグナルをtrapしました。
trap_exit.shを終了します.
#↑INT シグナルで終了した場合も EXIT シグナルは trap できる。

このテクニックは exit するルートが複数存在し、かつ終了時に共通の処理を行う必要があるシェルスクリプトにおいては非常に有効である。