UNIX のデーモンプロセスについて勉強しています。デーモンプロセスを作るには、
1. fork する (子プロセスを作って親は死ぬ)
2. setsid する (子プロセスを制御端末から切り離す)
3. もう一度 fork する (セッションリーダーではない孫プロセスを作って子は死ぬ)
という手順を踏むのだと、後述のブログ記事等で知りました (chdir, umask, STDIN/STDOUT/STDERR などの取り扱いは、今回は注目しないので省略します)
この 3. でやっている二度目の fork が必要な根拠として、セッションリーダーであるデーモンプロセス (1. で fork した子プロセス) に制御端末を割り当てられてしまうとまずいためだ、という理由が挙げられています (シグナルを送られてプロセスを操られてしまうとか)。まずいケースを実体験したいのでそのようなプロセスに制御端末を割り当ててみたいのですが、やり方がわかりませんので教えてください。
https://github.com/kyanny/sample-daemon に Ruby で書いたサンプルプログラムがありますのでこちらもあわせてご覧ください。
FreeBSDで確認しました。linuxだとioctlがいらないかもしれません。
pythonが一番楽なのでまずはpythonで。
#!/usr/local/bin/python import os, pty, sys, time, fcntl, termios if os.fork() <> 0: sys.exit() os.setsid() master, fd = pty.openpty() #fd = os.open('/dev/ttyv9', os.O_RDWR) fcntl.ioctl(fd, termios.TIOCSCTTY, 0) while True: time.sleep(1)
擬似端末を取得して割り当てます。
既にプロセスに割り当てられている端末は別プロセスに割り当てが出来ませんので、未使用の端末か擬似端末を使用します。
linux及びBSD系では openpty(3) により擬似端末を取得できます。
SYSV系及びlinuxでは 端末が未割り当てのセッションリーダーが端末を開くと自動的に割り当てられるとのことですが、環境がないので確かめていません。
もし open(2) または openpty(3) の時点で端末割り当てが行われているなら、次の ioctl(2) は失敗すると思いますので、コメントアウトしてください。
BSD系では ioctl(2) のTIOCSCTTYにより明示的に割り当てます。
二度目のforkの後では失敗することも確認しました。
なお、コメントアウトしてありますが、擬似端末ではなく実際の端末を開いてもOKですが、パーミッション上rootでなければならないと思います。確かめる際はopenで開く端末を未使用の存在する端末に変更してください。
perl, rubyは openpty(3) がないのと、TIOCSCTTYが定義されていないようなので通常の端末を開いて、ioctlは数字指定で行いました。参考に書きますが適宜環境に合わせて変更してください。
#!/usr/bin/env ruby exit if fork Process.setsid open('/dev/ttyv9', File::RDWR){|f| f.ioctl(0x20007461) sleep 1 while true }
#!/usr/local/bin/perl use strict; use warnings; use POSIX; exit if (fork != 0); setsid or die $!; sysopen(HANDLE, '/dev/ttyv9', O_RDWR) or die $!; ioctl(HANDLE, 0x20007461, 0) or die $!; sleep(1) while (1);
ありがとうございます! Python のコードを Linux で動かして、端末が割り当てられることを確認できました。どういう理屈なのかまで含めて説明してもらって、とてもよくわかりました。 openpty や TIOCSCTTY などのポインタも示していただけたので、さらに自分でも調べてみようと思います。
2011/11/03 01:40:17