kubo39's blog

ただの雑記です。

spawnProcessのめも

外部プロセス起動のうち、もっとも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)を試して、ダメなら総当りしてるよ
  • 子プロセス側の終了を待つのはユーザが任意のタイミングで行うよ