HTTP サーバみたいにずっと動きっぱなしのプログラムを デーモン とか呼んでた時代もありましたが、最近は聞かない気がします。

とはいえ、Go や Rust の人気も出てきた昨今、割とデーモンになるようなプログラムを書く機会も増えているのではないかと感じています。 例えば、JSONHTTP でやり取りするようなサーバアプリケーションや、 gRPC サーバアプリケーションを書いた場合には、基本的には動かしっぱなしのデーモンとしてアプリケーションを作るのが一般的です。

そんなプログラムを書くときに大事になってくる要素の一つがシグナル処理です。

あなたのプログラムは起動した後、任意のタイミングでうまく停止できる必要があります。 一般的には、アプリケーションは停止シグナルを受け取ったら停止するように作ります。 具体的には起動後に Ctrl + C を入力されたり、 OS から kill コマンド で停止をリクエストされたりした時に アプリケーションはシグナルを OS から受け取りますので、受け取ったシグナルに応じた停止処理を作るのが良いでしょう。

本文書では、きちんと停止処理を書くために、改めてシグナル処理について振り返ってみます。

シグナルってなんだっけ

一般的に、動いているプログラムをプロセスと呼び、このプロセスはたまに OS から割り込みを受けます。 この割り込みが「シグナル」です。

例えば、あなたが httpd サーバを動かしている最中に、誰かがそのマシンの電源ボタンを押してシャットダウンを開始したとしたら httpd サーバには SIGHUP というシグナルが OS から送られます。

他にも、起動中にあなたが kill your_server みたいにプロセスを終了させたり、Ctrl + C で終了を試みたりした場合も、 OS はプロセスに対して SIGINT というシグナルを送ります。

あなたがサーバを書くときに、シグナルを処理しなくても、最終的には OS によってプロセスは終了させられることにはなると思いますが、 きちんと処理した方がスマートです。 (何がどうスマートかというと、終了時の処理をきちんと書いてあるあたりがスマートです)

そんなわけで、世界中の大抵のサーバプログラムでは、シグナル制御がきちんと書かれています。 例えば、 Ruby on Rails の rails コマンドで SIGINT を処理しているのはこの辺 です。

シグナルの種類

シグナルは OS によって定義されており、OS によってまちまちです。 とはいえ、ある程度は共通化されており、大体の OS が従っている POSIX のシグナル には 30 強のシグナルが定義されています。 このうち、デーモンになるプログラムを書く際に、よく使うのは2−3種類です。

この、一般的な業務アプリを書くサーバプログラマーがよく出会う2-3種類を順に上げていくと以下の通りとなります。

  • SIGINT (割り込み, interrupt): 端末、つまりターミナルとかから割り込みキー (通常は Ctrl + C) が押されたときに発生する
  • SIGTERM (終了, terminate): 誰かが kill コマンド実行するとかによりプロセスを終了するよう OS が判断したときに発生する
  • SIGHUP (停止, hang-up): 端末が閉じられた時とかに発生するシグナル。例えば、無限ループするプログラムを実行中とかに、ターミナルを閉じたりするとこのシグナルが発生し、無限ループ中のプログラムは終了する。

自分でシグナル処理をアプリケーションに書くのが SIGINT, SIGTERM あたりで、 SIGHUP は、どちらかというと SIGHUP が発生しないように nohup コマンドを使うときに出くわすことが多い印象です。

少し頻度は下がりますが、以下のシグナルも記憶に留めておくと良いでしょう。

  • SIGKILL (強制終了, kill): 強制終了が命じられた時に発生するシグナル。 kill -9 PID などで発生する。
  • SIGQUIT (終了及びコアダンプ, quit): 終了し、コアダンプを書き出すように要求された時のシグナル。通常は Ctrl + \ で発生させる

その他にも、セグメンテーション違反が起きたときの SIGSEGV や、Ctrl + Z 押したときの SIGSTP などもありますが、あまり自分で処理するプログラムを書くことはない印象です。

その他のシグナルについては、 wikipedia を読むのが一番わかりやすいと思います。

各言語でのシグナル制御

各言語でのシグナル処理の実装例を、個別のページに記しました。 適宜、読んでいただければ幸いです。