シェルスクリプトの基礎 - シグナルとtrapコマンド

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動

概要

シグナルとは、プロセス間で通信を行うための仕組みであり、Linuxカーネルに実装されている。
実行中のプロセスの処理を一時的に止めて、別のプロセスを処理する場合等に使用される。

シグナルは、キーボードからの割り込み(SIGINT)や浮動小数点例外(SIGFPE)、プロセスの終了(SIGTERM)等、30種類以上ある。
これらのシグナルは、プログラム(プロセス)自身の動作によって発生する同期シグナル、外的な要因で発生する非同期シグナルの2種類ある。

シグナルの動作を以下に示す。

  1. あるプロセスの実行中において、処理を割り込ませたい場合、カーネルがシグナルを発生させる。
  2. シグナルが発生すると実行中のプロセスが中断され,特定の処理が実行される。


この特定の処理のことを、シグナルハンドラと呼ぶ。
シグナルは突然発生するため、発生するシグナルごとに実行する処理を、予めシグナルハンドラとして登録する。

使用可能なシグナルの一覧は、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
...略



シグナルを送出する

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

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

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


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

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

シグナル番号またはシグナル名を省略する場合、標準で15番シグナル(TERM)が送出される。

sleep 100000 &  # sleepコマンドに&を付加してバックグラウンドで実行する

[1] 5355


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

5355 sleep 100000


kill -15 5355  # TERMシグナルを送出する


pgrep -lf sleep  # 再検索すると終了していることを確認できる

[1]+  終了しました            sleep 100000


下表に、各シグナルが持っている意味を示す。

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

シグナル番号 シグナルが持つ意味
0 プロセス終了時に、プロセスが自分自身に対して送出するEXITシグナル。
1 XWindowのクローズやデーモンのリセットに使用されるハングアップシグナル。
2 [Ctrl]+[C]キーや[Delete]キーを押下した時に発生する割り込みシグナル。
3 [Ctrl]+[\]キーを押下した時に発生するクイットシグナル。
9 プロセスを強制終了するためのキルシグナル。
強制終了であるため、trapできない。
15 プロセスを終了させるための終了シグナル。
killコマンドは標準でこのシグナルを使用する。
(kill <PID>は、kill -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コマンドを使用することで捕捉することができる。

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 "" 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)もあるが、9番(SIGKILL)は指定してもtrapすることができない。
したがって、シェルスクリプト内でtrap処理を行う場合は、1、2、3、15のみを指定する。

以下の例では、trapコマンドを使用して、終了前にゴミ掃除を実行している。

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

 #!/usr/bin/env sh
 
 # ゴミ掃除用の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
 
 # 出力
 作成されたファイルを確認する
 -rw-r--r-- 1 sunone sunone 11  6月 2日 14:01 temp.9535
 [Ctrl]+[C]キーを押下してください
 
 # ここで、[Ctrl]+[C]キーを押下する
 
 trapped.  # トラップ処理が実行されて、一時ファイルが削除される



trapコマンドの応用例 2

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

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


シェルスクリプトが終了する時に終了メッセージを表示するような処理は、予め、EXITシグナルをtrapすることで実現できる。
また、trap '<コマンド>' 0のように0番を指定しても結果は同じになるが、
trap処理が終了処理であることが明示的に分かるEXITを指定する方がよい。

以下の例では、EXITシグナルをtrapして終了処理を実行している。
このテクニックは、exitコマンドを実行する分岐が複数存在して、かつ、終了時に共通の処理を行う必要がある場合において非常に有効である。

 #!/usr/bin/env sh
 
 # EXITシグナルをtrapして終了メッセージを指定する
 trap "echo '`basename $0`を終了します'" EXIT
 
 # 他のシグナルもtrapしておく
 trap "echo '他のシグナルをtrapしました'" 1 2 3 15
 
 echo "[Ctrl]+[C]キーを押下してください"
 
 # [Ctrl]+[C]キーで終了するため、sleepする
 sleep 10
 
 exit 0
 
 # 出力
 [Ctrl]+[C]キーを押下してください
 
 # ここで、[Ctrl]+[C]キーを押下する
 
 他のシグナルをtrapしました
 xxx.shを終了します  # INTシグナルで終了した場合でも、EXITシグナルはtrapできる