fork()
システムコール は、実行中のプロセスから子プロセスを複製します。 2つのプロセスは、PIDを除いて同じです。
当然、プロセスがヒープに書き込むのではなく、ヒープから読み取るだけの場合、ヒープをコピーすることはメモリの大きな浪費になります。
プロセスヒープ全体がコピーされますか?書き込みのみがヒープのコピーをトリガーするように最適化されていますか?
fork()
の-entiretyは、mmap/copy on writeを使用して実装されます。
これは、ヒープだけでなく、共有ライブラリ、スタック、BSS領域にも影響します。
つまり、結果として生じる2つのプロセス(親と子)が実際にメモリ範囲への書き込みを開始するまで、フォークは非常に軽量な操作です。この機能は、フォークボムの致命性の主な原因です。カーネルがページの複製と差別化で過負荷になる前に、プロセスが多すぎます。
カーネルがハードコピーを実行する操作の例(デバイスドライバーは例外です)を最新のOSで見つけるのは難しいでしょう-使用するのははるかに簡単で効率的ですVM機能性。
execve()
でさえ、本質的に「バイナリ/ ld.so/whatnotをmmapしてから実行してください」であり、VMはRAMへのプロセスの実際のロードを処理します_と実行。ローカルの初期化されていない変数は、「ゼロページ」からmmapedになります-ゼロを含む特別な読み取り専用のコピーオンライトページ、ローカルの初期化された変数は、バイナリファイル自体からmmaped(再びコピーオンライト)されます。等.
Linuxカーネルは、fork()
が呼び出されたときにコピーオンライトを実装します。 syscallが実行されると、親と子が共有するページは読み取り専用としてマークされます。
読み取り専用ページで書き込みを実行すると、2つのプロセス間でメモリが同一でなくなるため、ページがコピーされます。したがって、読み取り操作のみが実行されている場合、ページはまったくコピーされません。
Linuxはコピーオンライトを行います。 fork
が新しいプロセスを作成すると、割り当てられたページは読み取り専用としてマークされ、親と子の間で共有されます。それらのいずれかがページを変更しようとすると、ページフォールトが生成され、その結果、ページがコピーされ、ページテーブルが適切に調整されます。