hacomono TECH BLOG

フィットネスクラブ・スクールなど施設・店舗のための会員管理・予約・決済システム「hacomono」 開発チームの技術ブログ

Unixのシグナルとは?

開発チームのたなしゅん(@_tanashun)と申します。

Unixのシグナルについてよくわかっていない部分があったので、よく目にするシグナルをピックアップして学習しました。今回はそこで得た知見をブログに残したいと思います。

そもそもシグナルとは?

Wikipediaからの引用ですが、

シグナル(英: signal)とは、Unix系(POSIX標準に類似の)オペレーティングシステム (OS) における、限定的なプロセス間通信の形式を使って、プロセスに対し、非同期で、イベントの発生を伝える機構である。

と記載されています。

ja.wikipedia.org

簡単に要約すると、”プロセス同士が行う命令体系” みたいな感じになりそうです。

シグナルを受け取ったプロセスは、受け取ったシグナルに応じた動作を実行します。

シグナルの種類としてある程度標準化されている部分もあり、下記記事では18個紹介されています。

atmarkit.itmedia.co.jp

シグナルの送り方

まずはbashを例に、プロセスに対してのシグナルの送り方を見ていきます。

以下はSIGTERMを送るコマンドの例です。

kill -TERM [プロセスID]

各シグナルには数字が当てはめられており、SIGTERMを数値で指定する場合は以下のようになります。

kill -15 [プロセスID]

また、シグナル名や数値を指定せずに実行する場合もSIGTERMが送られます。

kill [プロセスID]

各シグナルの使い分けについて

シグナルには種類があり、デフォルトの動作として各シグナル毎に役割が定義されています。

今回は数ある命令の中でも、SIGHUP、SIGINT、SIGKILL、SIGTERMの4つについて書いていきたいと思います。

SIGHUP

端末を終了した時にその端末から起動された子プロセスに対して送られます。

ただし、よく利用されるケースとしては、プロセスに対して再起動をかけたい時に利用されるようです。

SIGHUPを捕捉しているようなプログラムでは、プロセスがバックグラウンドで動いている状態で、設定ファイルを再読込させたいケースなどで活用できます。

SIGINT

bashのデフォルト動作では、キーボードで”Ctrl + c”を入力した際にSIGINTが送られます。

プロセスを途中で中断させたい場合に利用されます。

キーボード割り込みのシグナルのため、フォアグラウンドで実行しているプロセスに対して利用できます。

SIGKILL

応答しなくなったプログラムに対してSIGKILLを実行すると、プロセスが強制終了されます。

他のコマンドで終了できなくなっているプロセスが存在する場合でも、このシグナルを使えば強制的にプロセスを終了させることができます。

また、このシグナルだけは後述するtrapコマンドでシグナルを捕捉することはできません。

SIGTERM

プロセスを終了させる目的で利用されます。

前述のbashの例にあったkillコマンドで、シグナルの種類を指定しない場合はデフォルトでSIGTERMが送られます。

シグナルを捕捉する方法

プロセスがシグナルを受け取った時に特定の処理を実行したい場合にtrapというコマンドを利用できます。

以下はbashでSIGINTを受け取った時にメッセージを出力する例になります。

trap 'echo "SIGINTを受信しました。"' SIGINT

これだけだとSIGINTをこのプログラムに送信する前にプロセスが終了してしまうので、以下のように無限ループのコードと合わせて確認するとわかりやすいです。

このスクリプトを実行中に実行した端末から ”Ctrl + c” でSIGINTを送ると、trapで捕捉している部分のメッセージが出力されます。

trap 'echo "SIGINTを受信しました。"' SIGINT

while true; do
    echo "ループ実行中"
    sleep 1
done

このプログラムはSIGINTを送っても止まらないため、以下の手順で終了させる必要があります。

  1. ”Ctrl + z”でプロセスを停止 (これもシグナルの一種であり、シグナル名はSIGTSTPです)
  2. 停止中のプロセスにSIGTERMを送って終了させる

コマンドの例

Ctrl + zを入力

^Zzsh: suspended  sleep 1 <- SIGTSTPシグナルによって停止される

ps
43042 ttys003    0:00.00 sleep 1 <- 停止中のプロセス

kill -term 43042 <- SIGTERMで終了させる

まとめ

ローカルで暴走してしまったプロセスを終了させる時になんとなくkillコマンドを利用していましたが、killコマンドには多くの種類があり、それぞれ役割に応じた動作が実行されることを今回学習できました。

普段の開発業務でシグナルを意識して開発する時はあまりないですが、バックグラウンドジョブを作る時やWEBサーバーを触る時などは知っていると役立つかと思います。

公式ドキュメントを見つつ、各アプリケーションがシグナルに応じてどのような動作をするように作られているのかを見ておくと適切なハンドリングができそうです。