先日webrickのソースを少し読んだのですが、daemonを作る部分がよくわかりませんでした。
def Daemon.start exit!(0) if fork Process::setsid exit!(0) if fork Dir::chdir("/") File::umask(0) STDIN.reopen("/dev/null") STDOUT.reopen("/dev/null", "w") STDERR.reopen("/dev/null", "w") yield if block_given? end
具体的にわからない部分を書くと
- Process::setsidってなに?
- なんでfork二回もやってるの?
- その後のchdirとかumaskとかreopenとかは何でやってるんだろう?
のようなところ。つまりほぼ全部ですね・・・><
そこでいつものように調べました。
Process::setsidってなに?
新しいセッションを生成するメソッド。
セッションとは
- ユーザがログインした時に作られるプロセスの集合。
- 各セッションは基本的にひとつの制御端末を持つ。
- 制御端末を持たないセッションはどのユーザの制御下にもなく、daemonプロセスと呼ばれる。
なんでfork二回もやってるの?
一度目のfork
Process::setsidを成功させるため。
まず、セッションを生成するプロセスはプロセスグループ*1のリーダー"以外"でなくてはならないという決まりがあります*2。そして、新規プロセスは新しいプロセスグループにプロセスリーダーとして配置されるそう。通常、プログラムは起動時に新しくプロセスが作られるので、最初にforkをしておかないと
デーモンを作るプログラムを起動→そのプロセスがプロセスリーダーになる→プロセスリーダからProcess::setsidが呼ばれる→セッション作成失敗
となるわけです。なのでforkすることによってリーダー"ではない"プロセスを作り、そのプロセス上でProcess::setsidを実行しています。
二度目のfork
制御端末を関連づけできなくさせるため。
セッション内には、セッションリーダーと呼ばれるプロセスがあり、このプロセスが制御端末とやりとりをします。
Process::setsidにより作られたセッション(およびセッションリーダー)は制御端末を持っていないのですが、セッションリーダーが端末デバイスを開くと、それが制御端末になってしまうのだそう。そこでセッションリーダーのプロセスを消すことによって、制御端末を関連づけさせることができなくなります。
その後のchdirとかumaskとかは何でやってるんだろう?
chdir
どのディレクトリも使用中ではないことを確実にするため。この処理をしないと、デーモンのカレントディレクトリは操作ができなくなってしまうらしい。
umask
プログラム中でファイルを生成する場合のumask値を揃えるため。ファイルを作らない場合などは特に設定しなくても問題ないようです。
ちなみにRuby1.9だと
Process.daemonが使えるようになっているので、上のコードは書かなくてもいいみたい。
Process - Rubyリファレンスマニュアル