外部プロセス起動のうち、もっともgenericなspawnProcessを読んでメモ書き。読んだのはPosix側の処理だけです。
spawnProcess
std.processのspawnProcess関数がそれ。実体はspawnProcessImpl関数。
fork(2)した後をみていく。まずは親側。
といっても大したことはしてなくて、条件によってstdin/stdout/stderのredirect先のfdを閉じて子プロセスのpidを返してるだけ。 waitは他の(つまりspawnProcess関数の外側で)処理でやる。子の終了を待つのはユーザの責任。
次に子側をみる。
まずstdin/stdout/stderrのfdをdup2(2)してる。それぞれのfdをデフォルトのfd(0,1,2)にredirectしてる。 dup2の挙動は古いfdと新しいfdが同じならなにもしない、なぜわざわざやってるかというとこれは 起動するプロセスがデフォルトのstdin/stdout/stderrのfdを期待するからなんだな。
一応コメントをみると、
// Redirect streams and close the old file descriptors. // In the case that stderr is redirected to stdout, we need // to backup the file descriptor since stdout may be redirected // as well.
その後標準入出力にかんしてはsetCLOEXECでclose-on-execフラグを外してfd継承を強制.
// Ensure that the standard streams aren't closed on execute, and // optionally close all other file descriptors.
fd継承するかしないかで処理がわかれてる。
しない方だけみてく。
まずプロセス内でopen可能なfdの最大数を取得。 その後可能ならpoll(2)を使って0,1,2以外のfdをcloseしていく。
// Call poll() to see which ones are actually open: // Done as an internal function because MacOS won't allow // alloca and exceptions to mix.
OSXはつらい。
poll(2)がだめな場合は順に全部closeしていくようだ。つらい。
それが終わるとexecve(2)で実行する。 execve(2)が失敗した場合はperror(3)でエラー出力して_exitで即座に終了する。
まとめ
- ふつうにfork(2)を使っているよ
- 子側はstdin/stdout/stderrをデフォルトの0,1,2番にredirectするよ
- 子側fd継承をしないときは明示的に他のfdをcloseしてるよ
- 子側fdをcloseするときはpoll(2)を試して、ダメなら総当りしてるよ
- 子プロセス側の終了を待つのはユーザが任意のタイミングで行うよ