■ Ruby で daemon プロセスを

setsid(2) のサポートが行なわれたときに記念に書いたものです。 現在は Ruby 1.9 系には Process#daemon があります。 これが標準で使えるようになるまでは daemon の作成に Process#setsid を使いましょう。

ruby-1.1b9_29 から setsid(2) がサポートされました(現在 1.8.6 がリリースされています)。 これで手軽に daemon プロセスがつくれます。 スクリプト言語ですが、手軽にちょっとしたサービスを実施できるようになるのはうれしいですね。

ここでの話は UNIX 系の OS が前提になりますが、POSIX の setsid をサポートしている場合には、同じことが可能かも。

また、考え方自身は C でプログラムをつくるときなどと共通です。 ただし、Ruby で実現するのはとても簡単なんですよ。

■ daemon プロセスって?

普段 Linux などを使用していると、自分が起動した覚えがないプロセスがいくつか動いていると思います。 その中の多くのものは daemon プロセスと呼ばれるものです。 サービスのために定期的に動くものや、サービスの要求を待っていて答えるものなどがあります。 有名なところでは、

などがあります。 ここでは、名前の最後に「d」がついていますが、これは偶然ではなく daemon を意味しています。 HTTP をサポートする daemon なので httpd なのです。

つまり、daemon プロセスはあるサービスを実施するために、常に常駐しているプロセスのことを示します。

■ daemon プロセスの作り方

daemon プロセスは簡単につくれるでしょうか? Linux などでは、バックグラウンドでプロセスを起動するのにコマンドラインから次のよう指定します。

$ checkprogram &
このように起動すると、プログラムは動きだし、必要なサービスを実施します。 ネットワークでサービスしたり、ファイルを監視したりするプログラムなどが作成できるはずです。 では、普通に作成したプログラムを、このように起動するのと、daemon プロセスは何が違うでしょう?

ps コマンドで、プロセスの状態をみてみます。

USER       PID %CPU %MEM  SIZE   RSS TTY STAT START   TIME COMMAND
root       111  0.0  0.5   956   424  ?  S   07:07   0:00 syslogd 
root       153  0.0  0.4   936   368  ?  S   07:07   0:00 inetd
root       190  0.0  0.9  1364   716  ?  S   07:07   0:00 httpd 
nobody     201  0.0  1.0  1388   796  ?  S   07:07   0:00 httpd 
nobody     202  0.0  1.0  1388   816  ?  S   07:07   0:00 httpd 
nobody     203  0.0  0.9  1364   716  ?  S   07:07   0:00 httpd 
nobody     204  0.0  0.9  1364   716  ?  S   07:07   0:00 httpd 
nobody     205  0.0  0.9  1364   716  ?  S   07:07   0:00 httpd 
nobody     206  0.0  0.9  1364   716  ?  S   07:07   0:00 httpd 
nobody     207  0.0  0.9  1364   716  ?  S   07:07   0:00 httpd 
nobody     208  0.0  0.8  1364   712  ?  S   07:07   0:00 httpd 
nobody     209  0.0  0.8  1364   704  ?  S   07:07   0:00 httpd 
nobody     210  0.0  0.8  1364   704  ?  S   07:07   0:00 httpd 
tetsu     3121  0.0  0.5  1032   444  p0 R   09:31   0:00 ps aux 
tetsu     3122  0.0  0.6  1272   508  p0 S   09:31   0:00 less 
httpd などの daemon と psless などの一般のコマンドです。 どこか違いませんか?

少しややこしい話になりますが、deamon プロセスはプロセスがターミナルを持っていません。 ps の出力で TTY のエントリが「?」になっています。 これがプロセスが所属するターミナルがないということを意味しています。 ここでの「ターミナル(端末)」は、プロセスが所属する「制御端末」と呼ばれるものです。 このことが動いているプログラム(プロセス)にどう影響するのでしょう?

Linux などの UNIX 系の OS では、 一般にプロセスが、所属するターミナル(制御端末)を持っています。 起動されたターミナルが、プロセスグループを構成する単位になっています。 プロセスグループは、シグナルの影響する範囲に関係しています。

ログインすると、シェルが起動します。 そのログインシェルが属するターミナル(制御端末)が一つの単位(プロセスグループ)になっています。 そのため、ログインシェルから抜ける(ログアウトする)とき、起動していたプロセス(バックグラウンドプロセスを含め)にシグナルが送られます。 このようにして、ログアウトしたことをプロセスグループに伝える仕組みを持っています。

daemon プロセスは、このプロセスグループを独立したものを持つようにします。 そうしないと、他のプロセスのシグナルを不用意に受けてしまう可能性があるからです。

ちょっとややこしいんですが、「お決まり」なので... 詳しく勉強されたい方はすばらしくいい参考文献が 2 冊あります。 現在は絶版になっている可能性が高いのですが「W.Richard Stevens」さんが書かれた本は、どれもすばらしいのでみつけたら読んでみてください。

同じ著者の「UNIX ネットワークプログラミング 第二版 Vol.1 ネットワーク API: ソケットと XTI」。

制御ターミナルを持たないプロセスが、制御ターミナルに属していないターミナルをオープンすると、それが制御ターミナルになる。
これを予防するために、fork(2) を二回実行するそうです。 現在は setsid(2)(注意のところを読んでみてください) を使った場合はどうか明示がないのですが。 私は「なせ二回 fork(2) するのか?」と思っていたので、これでようやく納得できたのでした。 こういう知識って、daemon プロセスを作成するときに大切ですけど、明示された情報源ってすくないですね。 daemon 作っています?

setsid をキーワードに Google すると、いろいろ情報がみつかります。 気になったら読んでみるといいと思います。

■ Ruby で daemon プロセスをつくる

最初に setsid を使わない例です。 何もせずに 10 秒待っているだけのスクリプトです。

exit if fork

sleep 10

exit
これを ps コマンドで確認すると次のようになっています。
tetsu     3124  0.0  1.1  1756   948  p0 S   09:47   0:00 ruby daemon.rb 

次に Process.setsid を使った例です。 daemon になっていますよ。

exit if fork

Process.setsid

sleep 10

exit
これを ps コマンドで確認すると次のようになっています。
tetsu     3161  0.0  1.1  1656   920  ?  S   09:56   0:00 ruby daemon.rb

■ Ruby 1.1b9_29 で実行する前に

ruby-1.1b9_29 だけは、次のパッチを行ってから使ってください。 ruby-1.1b9_30 以降は修正済です。

$ diff -u process.c.org process.c
--- process.c.org       Fri Jul  3 16:06:35 1998
+++ process.c   Sat Jul  4 10:03:18 1998
@@ -858,7 +858,7 @@
     int pid = setsid();
 
     if (pid < 0) rb_sys_fail(0);
-    return NUM2INT(pid);
+    return INT2FIX(pid);
 #else
     rb_notimplement();
 #endif


渡辺哲也(WATANABE Tetsuya): Tetsuya.WATANABE atmark nifty.com