2009-04-15 [長年日記]
λ. Ruby とプロセス: spawn について
akrさんのspawnの話を聴きにいった。 Ruby関係の参加者が沢山いるだろうと予想して行ったら、参加者はすごく少なかった。勿体無いなぁ。 私の知識は、想定される聴衆 level 2 「fork と exec なら大学で習った (shell を作る実習とか)」程度なので、「ほえー」という感じだったが、面白かった。
以下はメモ。
- Rubyのプロセス起動のAPIは、帯に短しタスキに長し、というか短いものしかない。
- fork/exec
- 当時のシステムコールのデザイン。カーネルはアトミックなものを提供し、複雑なものはライブラリが提供する。
- ruby 1.9 のタイマースレッド
- スレッドの切り替え
- シグナルはタイマースレッドが受ける
- spawnで十分なのか?
- コマンド実行にforkを使わなくていいじゃん、というのが今回の話。fork後execしないようなものはまた別の話。
- 最終的なFDのマッピングを実現するために、一時的なFDが必要になる事があるので、FDの空きが足らないと動かない。ただ、Rubyは子プロセスでのエラーを通知するためにパイプを使っていて、そういう意味ではもともとダメなので気にしない。「もうだめなんで」「毒を喰らわば皿まで」
- 余計なFDを閉じないとまずいことになる例
- sortを呼び出してソートを行う場合、sortが(知らずに)書き込み側のFDを持ってると、読み込み側のFDをEOFまで読めない
- popen4, popen5
- pythonでもpopen5まで提案されたが、さすがにsubprocessという名前になった。
- systemの実行中にinterruptを受けたら、子プロセスが死ぬだけで親は死なない
- いつも使っているようなものでも、abstractionを変えるべきという一つの例
- Windowsの実装はCreateProcessでうまくいくかと思ったら、親プロセスの属性を一時的にちょっと変えた状態でCreateProcessして、親プロセスを戻すという風になっている。
- 親で属性を変えたときに安全に復帰できるか?
- 例えばカレントディレクトリに戻れるか? (fchdirがあれば安全に戻れる)
- 「race condition だから、バレなければOK」
- 設定可能な属性は実は取捨選択してる。uid, gid周りは意図して避けた。特権のところはruby使うな。あと、シグナルのマスクとか。
- 子プロセス側での属性設定、Windowsでも止まった状態で起動して、スレッドを注入して...とかやると出来る。
- Windows ではFDは3番目以降は継承されない。カーネルオブジェクトは継承できるが、カーネルオブジェクトからFDへの対応が継承できない。前述のテクニックで、無理やり初期化すれば出来るらしい。しかし、WindowsにはFDを外から受け取るようなコマンド(e.g. valgrind)はそもそも無いので継承しても無意味。
- 色々なプラットフォーム
- MacOS : 複数のスレッドが動いているとexecがENOTSUPで失敗する。
- FreeBSD 7 : スレッドを使っていると、forkだかexecだかが動からないらしい。
- NetBSD 4 : fork したプロセスでスレッドが動かない。n:m のスレッド管理情報がメモリマップで管理されていて、共有されてまずいらしいとか。
- NetBSD 5 : 動くらしい。
- 動かないっていってくれる方がありがたいのでは?
- 想定している前提
- fork → exec ではどのOSでもサポートするであろう。
- マルチスレッドの時にfork出来るか? さすがにシステム関数は使いたいから、動くだろう。
- spawnはそこに毛が生えた程度をサポート
- setenvはasyncsafeではないので、環境変数の設定は本当は親でやる必要があるかも。