https://redmine.ruby-lang.org/
https://redmine.ruby-lang.org/favicon.ico?1711330511
2012-09-15T13:12:03Z
Ruby Issue Tracking System
Ruby master - Bug #6441: IO.pipe on ENFILE
https://redmine.ruby-lang.org/issues/6441?journal_id=29318
2012-09-15T13:12:03Z
authorNari (Narihiro Nakamura)
authorNari@gmail.com
<ul></ul><p>すみません、この問題をよく理解できてないので何点か質問させてください。<br>
GC側をどのように直せばいいかいまいちわかっておらず、悩んでいます…。</p>
<p>naruse (Yui NARUSE) wrote:</p>
<blockquote>
<p>今の Ruby は open(2) などで、errno=ENFILE が発生した場合、<br>
すなわち fd を使いきっている場合には、rb_gc() を呼び、IO オブジェクトを GC して、<br>
fd が解放されないか試み、それでもダメだったら諦めるとしています。</p>
<p>しかし、IO.pipe の場合はこれに失敗することがあります。</p>
</blockquote>
<p>失敗する、というのは「fd の解放を試み、ダメなら諦める」ということが<br>
IO.pipeでできないことがある、ということでしょうか?</p>
<blockquote>
<p>これは、lazy sweep が上記の目的のため T_FILE の場合は直ちに sweep することにしている所、<br>
pipe の場合はその例外にあたらないからです。</p>
</blockquote>
<p>その例外というのはpipeで作られるfdがT_FILEのオブジェクトにわりあてられ<br>
てないとかそういう話なのでしょうか。</p>
<p>また再現コードなどがあればいただけると嬉しいです。</p>
Ruby master - Bug #6441: IO.pipe on ENFILE
https://redmine.ruby-lang.org/issues/6441?journal_id=29478
2012-09-18T10:18:31Z
shyouhei (Shyouhei Urabe)
shyouhei@ruby-lang.org
<ul></ul><p>authorNari (Narihiro Nakamura) wrote:</p>
<blockquote>
<p>naruse (Yui NARUSE) wrote:</p>
<blockquote>
<p>今の Ruby は open(2) などで、errno=ENFILE が発生した場合、<br>
すなわち fd を使いきっている場合には、rb_gc() を呼び、IO オブジェクトを GC して、<br>
fd が解放されないか試み、それでもダメだったら諦めるとしています。</p>
<p>しかし、IO.pipe の場合はこれに失敗することがあります。</p>
</blockquote>
<p>失敗する、というのは「fd の解放を試み、ダメなら諦める」ということが<br>
IO.pipeでできないことがある、ということでしょうか?</p>
</blockquote>
<p>パイプがたくさんゴミになっている状況を考えてください。<br>
本来であれば、GCされれば、それらのゴミパイプ(=fd)は回収されるはずです。</p>
<p>さて、そのような状況下であらたにIO.pipeを呼んだところ、ENFILEになったとしましょう。<br>
この場合、fdが開放されることを期待してGCが走ります。<br>
しかしながら現在のrubyではlazy sweepがあるため、GCしたからといってすぐにはsweepされません。<br>
したがって、本来であればGCすることでゴミパイプが回収されてfdに余裕が出ることが期待されるところ、<br>
引き続きfdが枯渇した状況が続いてしまいます(=GCしても意味なかった)。</p>
<blockquote>
<blockquote>
<p>これは、lazy sweep が上記の目的のため T_FILE の場合は直ちに sweep することにしている所、<br>
pipe の場合はその例外にあたらないからです。</p>
</blockquote>
<p>その例外というのはpipeで作られるfdがT_FILEのオブジェクトにわりあてられ<br>
てないとかそういう話なのでしょうか。</p>
</blockquote>
<p>上記のような問題がなぜパイプ以外で発生しないかというと、普通のファイルの場合だけlazy sweepにハックが入っていて、<br>
「普通のファイルなら即座にsweep」ということになっているからです。という意味だと思われます。</p>
<blockquote>
<p>また再現コードなどがあればいただけると嬉しいです。</p>
</blockquote>
<p>zsh % ulimit -n 7 # 適当に小さい数字<br>
zsh % ruby -ve 'loop { IO.pipe }'<br>
ruby 2.0.0dev (2012-09-16 trunk 36984) [x86_64-linux]<br>
-e:1:in <code>pipe': Too many open files (Errno::EMFILE) from -e:1:in </code>block in '<br>
from -e:1:in <code>loop' from -e:1:in </code>'<br>
zsh: exit 1 ~/target/trunk/bin/ruby -ve 'loop { IO.pipe }'</p>
<p>ゴミを生じているだけですので本来であれば無限に実行が続くべきです。</p>
Ruby master - Bug #6441: IO.pipe on ENFILE
https://redmine.ruby-lang.org/issues/6441?journal_id=29513
2012-09-19T17:53:58Z
authorNari (Narihiro Nakamura)
authorNari@gmail.com
<ul></ul><p>shyouhei (Shyouhei Urabe) wrote:</p>
<blockquote>
<p>authorNari (Narihiro Nakamura) wrote:</p>
<blockquote>
<p>naruse (Yui NARUSE) wrote:</p>
<blockquote>
<p>今の Ruby は open(2) などで、errno=ENFILE が発生した場合、<br>
すなわち fd を使いきっている場合には、rb_gc() を呼び、IO オブジェクトを GC して、<br>
fd が解放されないか試み、それでもダメだったら諦めるとしています。</p>
<p>しかし、IO.pipe の場合はこれに失敗することがあります。</p>
</blockquote>
<p>失敗する、というのは「fd の解放を試み、ダメなら諦める」ということが<br>
IO.pipeでできないことがある、ということでしょうか?</p>
</blockquote>
<p>パイプがたくさんゴミになっている状況を考えてください。<br>
本来であれば、GCされれば、それらのゴミパイプ(=fd)は回収されるはずです。</p>
<p>さて、そのような状況下であらたにIO.pipeを呼んだところ、ENFILEになったとしましょう。<br>
この場合、fdが開放されることを期待してGCが走ります。<br>
しかしながら現在のrubyではlazy sweepがあるため、GCしたからといってすぐにはsweepされません。<br>
したがって、本来であればGCすることでゴミパイプが回収されてfdに余裕が出ることが期待されるところ、<br>
引き続きfdが枯渇した状況が続いてしまいます(=GCしても意味なかった)。</p>
</blockquote>
<p>ENFILEのときに呼び出されるrb_gc()はlazy sweepしないように作っていて、そ<br>
ういうことが起きないようになっているはずなのですが、実際に問題が発生し<br>
ているということは何かおかしそうですね。</p>
<blockquote>
<blockquote>
<blockquote>
<p>これは、lazy sweep が上記の目的のため T_FILE の場合は直ちに sweep することにしている所、<br>
pipe の場合はその例外にあたらないからです。</p>
</blockquote>
<p>その例外というのはpipeで作られるfdがT_FILEのオブジェクトにわりあてられ<br>
てないとかそういう話なのでしょうか。</p>
</blockquote>
<p>上記のような問題がなぜパイプ以外で発生しないかというと、普通のファイルの場合だけlazy sweepにハックが入っていて、<br>
「普通のファイルなら即座にsweep」ということになっているからです。という意味だと思われます。</p>
<blockquote>
<p>また再現コードなどがあればいただけると嬉しいです。</p>
</blockquote>
<p>zsh % ulimit -n 7 # 適当に小さい数字<br>
zsh % ruby -ve 'loop { IO.pipe }'<br>
ruby 2.0.0dev (2012-09-16 trunk 36984) [x86_64-linux]<br>
-e:1:in <code>pipe': Too many open files (Errno::EMFILE) from -e:1:in </code>block in '<br>
from -e:1:in <code>loop' from -e:1:in </code>'<br>
zsh: exit 1 ~/target/trunk/bin/ruby -ve 'loop { IO.pipe }'</p>
<p>ゴミを生じているだけですので本来であれば無限に実行が続くべきです。</p>
</blockquote>
<p>ありがとうございます。再現コードを基に調査してみます。</p>
Ruby master - Bug #6441: IO.pipe on ENFILE
https://redmine.ruby-lang.org/issues/6441?journal_id=29533
2012-09-19T21:25:50Z
nagachika (Tomoyuki Chikanaga)
nagachika00@gmail.com
<ul></ul><p>どうも IO.pipe で最後に開いた IO 2つは GC.start で手動で GC を実行しても回収されないみたいです。</p>
<p>$ ruby -ve 'GC.start;loop{IO.pipe.tap{|i|p i}.clear;GC.start}'"<br>
ruby 2.0.0dev (2012-08-08 trunk 36663) [x86_64-darwin10.8.0]<br>
[#<IO:fd 5>, #<IO:fd 6>]<br>
[#<IO:fd 7>, #<IO:fd 8>]<br>
[#<IO:fd 5>, #<IO:fd 6>]<br>
[#<IO:fd 7>, #<IO:fd 8>]<br>
[#<IO:fd 5>, #<IO:fd 6>]<br>
[#<IO:fd 7>, #<IO:fd 8>]<br>
...(以下繰り返し)<br>
なので ulimit -n 9 だと動き続けることができるようでした。<br>
はて、なんでしょう。</p>
Ruby master - Bug #6441: IO.pipe on ENFILE
https://redmine.ruby-lang.org/issues/6441?journal_id=29534
2012-09-19T23:32:13Z
nagachika (Tomoyuki Chikanaga)
nagachika00@gmail.com
<ul></ul><p>どこから mark されてるんだろうかと追跡してみたところ<br>
mark_current_machine_context() の rb_gc_mark_locations() から、つまり machine stack からでした。</p>
<p>また optflags=-O0 をつけるとすぐに回収されていて ulimit -n 7 でも動き続けることができたので、少なくともわたしの手元の環境ではマシンスタックにゴミ参照が残っていて回収されないだけのようでした。参考まで。</p>
Ruby master - Bug #6441: IO.pipe on ENFILE
https://redmine.ruby-lang.org/issues/6441?journal_id=29568
2012-09-20T14:06:07Z
authorNari (Narihiro Nakamura)
authorNari@gmail.com
<ul><li><strong>Status</strong> changed from <i>Assigned</i> to <i>Rejected</i></li></ul><p>私の環境でも調べて見ましたが、近永さんからご報告いただいたいのと同じ状況で、ゴミ参照が残っていて ulimit で絞り過ぎると落ちるみたいでした。<br>
これは仕方ないかなぁと思います。</p>
<p>追加の情報などがあればreopenをお願いします。</p>