From: WATANABE Tetsuya Date: 1998-11-30T09:22:53+09:00 Subject: [ruby-dev:3701] [BUG?] fork(2) and read(2) 渡辺です。 以下のような現象がでているので検討ください。 今朝会社で試したら、かなり転送サイズを大きくしないと再現し ませんでした。 Pentium II 266MHz では ------------------------------ msg = '0123456789' * 100 * 32 * 2 0.upto(1000) do c.request(msg) end ------------------------------ です。自宅のシステムは Pentium 166MHz です。 ■ 現象 fork した場合、socket で通信中に子プロセスが、SIGVTALRM で 終了してしまう。 ソケットを使用した通信のスクリプトに fork と setsid でデー モンプロセスになるように設定する。fork を使用する前は、正 常に動作していたスクリプトが、fork することで動作しなくな る。 症状としては、通信中に中途半端なところで中断してしまう。 strace(1) などのツールを使って確認してみたところ、fork(2) 後 setitimer(2) でインターバルタイマの初期化が行われ、シグ ナルハンドラが設定されていないため中断していることがわかる。 process.c を確認したら、Linux のシステムだけが setitimer(2) (after_exec())していることに気がつきパッチを 作成。 ■ 環境 Linux 2.0.36 glibc-2.0.7 (TurboLinux 2.0J) Ruby 1.1c9 ■ 再現スクリプト udp のポートとして 22222 使います。空いているとは思います が、一応確認してください(netstat -a で)。 もしかすると速い CPU では、udp で送るメッセージをもっと大 きくしないと再現しないかもしれません。というのは、メッセー ジを受けとる read(2) 中にインターバルタイマを受けて落ちる のが現象のためです。インターバルタイマで呼ばれる前に read(2) が終了してしまうと現象は起きないと思います。 #! /usr/local/bin/ruby # /home/tetsu/src/ruby/bug/fork.rb # Created: November 29,1998 Sunday 05:25:13 # Author: tetsu(WATANABE Tetsuya) # $Id$ # usage: require 'socket' class Ssample def initialize @udp = UDPsocket.open @udp.bind('localhost', 22222) @from = nil end def request ret = @udp.recvfrom(32 * 1024, 0) @from = ret[1] ret[0] end def reply(msg) @udp.send(msg, 0, *@from) end end class Csample def initialize @udp = UDPsocket.open @udp.connect('localhost', 22222) end # server module def request(msg) @udp.send(msg, 0) ret = @udp.recvfrom(32 * 1024, 0) @from = ret[1] ret[0] end end if Process.fork # p sleep 1 c = Csample.new print c.request('msg'), "\n" msg = '0123456789' * 100 0.upto(100) do c.request(msg) end print c.request('EXIT'), "\n" else # c s = Ssample.new while msg = s.request s.reply(msg) break if msg == 'EXIT' end end exit ■ 実行方法 $ strace -f ruby fork.rb 2> strace.txt ■ 結果 省略 [pid 3119] sendto(4, "01234567890123456789012345678901"..., 1000, 0, {sin_family=AF_INET, sin_port=htons(1115), sin_addr=inet_addr("127.0.0.1")}, 16) = 1000 [pid 3119] --- SIGVTALRM (Virtual time alarm) --- [pid 3119] +++ killed by SIGVTALRM +++ <... send resumed> ) = 1000 --- SIGCHLD (Child exited) --- 省略 子プロセスが、SIGVTALRM にて終了してしまっています。 ■ パッチ fork(2) の後で、どうして Linux だけが特別扱いになっている のか私には理由がわからないので、もしかするとこのパッチはま ずいかもしれません。 処理に問題がなければ、このパッチを採用してください。 --- process.c.org Tue Oct 6 11:45:00 1998 +++ process.c Sun Nov 29 06:30:04 1998 @@ -543,9 +543,6 @@ rb_secure(2); switch (pid = fork()) { case 0: -#ifdef linux - after_exec(); -#endif if (iterator_p()) { rb_yield(Qnil); _exit(0); ■ ということで socket でサービスするデーモンのスクリプトを書いて、一通り の処理ができるようになったので、デーモンにすべく fork と setsid を呼びだしました。そしたら、長いメッセージの場合中 途半端なところでプロセスが死んでしまって、処理がうまく行き ません。「あれ?」ということで... p.s. E-mail がたくさんたまっている月曜日は大変ですね。 まつもとさん。 -- WATANABE Tetsuya HP Japan e-mail Tetsuya_WATANABE@hp.com tetsu@jpn.hp.com