NetBSD ドキュメンテーション: なぜ伝統的な vfork()を実装したのか
序 (トップ)
vfork()
は、子プロセスがいずれ他のプログラムを exec()
し、
かつ親プロセスはそれが起きるまで待っているという、特別な状況で
用いるために設計されたものです。
伝統的な fork()
では、子プロセスに親プロセスの
すべてのページを複製するという著しいオーバーヘッドが生じます。
COW (コピーオンライト)が追加された
Mach VM システムでは
fork()
の負荷が大変軽くなり、
BSD 4.4では vfork()
は fork()
と
同義のものとされました。
NetBSD 1.3 以降では、伝統的な vfork()
が再実装されました。
UVMで COW を
よりよいものにするために多大な努力がおこなわれました。
しかしアドレス空間を共有する vfork()
が いまだに
勝ることが分かったのです。
これにより、200MHz PPro での libcのビルドの所要時間を
数秒削ることができます。
4.4BSDの vfork()
/exec()
を使った
vfork()
と COW の処理 (トップ)
親の vm_map をたどって、アドレス空間の書き込み可能な部分を COW とマークする。これは、 pmapを呼び出し、 PTEを修正し、 TLBをフラッシュすることを意味します。
子プロセスの vm_map を生成し、親の vm_map エントリーを子の vm_mapにコピーします。 場合によっては、 pmapを呼び出して親のページテーブルから このページテーブルに PTE をコピーします。
親をブロックします。
子が走ります。 PTEがコピー されてない場合には、 ページフォルトが起きて現在プログラムカウンタのあるテキストページの物理マッピングが得られます。
子は exec し、そして 作られたばかりのアドレス空間全体を unmapして 新しく作ります。 これは、親の vm_mapを辿って COWとマークされた部分を 非COWにする作業を伴います。
親のブロックを解きます。
親が走り、以前には R/W であって COWにするために R/Oとマークしたデータを 変更するとページフォルトを起こします。この時点ではデータのコピーは行なわれません。
アドレス空間共有を用いた
3.0BSD/NetBSD の vfork()
の処理 (トップ)
親プロセスの vmspace構造体への参照を得ます。
親をブロックします。
子が走ります。 親のページテーブルが用いられるので、 PTEは既に 有効でありページフォルトは起こりません。
子が exec し、親の vmspace構造体に対して持っていた参照を削除し、 新しいものを作ります。
親のブロックを解きます。
親が走ります。(親の vm_mapは変更されていないので、 ページフォルトは起こりません。)
このように、fork してさらに execするような場合には、 明らかに後者がより高速です。 たとえ良い COWアルゴリズムを使っているとしても、仮想空間を共有する 場合と比べて大変多くの作業を行なわねばなりません!
Back to NetBSD ドキュメンテーション: カーネル