プロセスを分岐すると、親プロセスのメモリがマップされ、書き込み時にコピーされることがわかります。作成する必要がある部分だけをコピーしますか、またはマップされたメモリ全体をコピーしますか?
答え1
短い答え:どちらもありません。カーネルはページ全体をコピーします。ページサイズは、使用しているアーキテクチャによって異なります(現在のアーキテクチャでは、複数のページサイズをサポートすることがよくあります)。 x86 64ビットアーキテクチャで実行されている場合、最も可能性の高いサイズは4KiBです。
より長い答え: カーネルとハードウェア仮想メモリシステムは、ページと呼ばれるフラグメントでメモリを処理します。たとえば、x86 64ビットアーキテクチャのデフォルトのページサイズは4KiBです。
この間、fork()
親メモリ空間の大部分は子プロセスと共有されます。このメモリ空間はページで構成されます。各共有ページは、その期間中にカーネルによって読み取り専用として表示されますfork()
。
その後fork()
完了誰でも親または子がメモリを修正しようとし、ページが読み取り専用としてマークされ、ハードウェアでページエラーを生成します。ページエラーは、カーネルに閉じ込められたCPU例外です(つまり、現在のプログラムの実行が停止し、CPUがカーネルで事前定義された例外ハンドラの実行を開始します)。
その後、カーネルはこれが書き込みコピーエラーであると判断し、新しいページを割り当てます。ページが読み取り/書き込みとしてマークされ、前のページの内容がコピーされます。その後、カーネルはそれをメモリ空間にマッピングします。その後、キャプチャコマンドでプロセス実行が再開されます。つまり、エラーの原因となったコマンドが再実行されます。今回はページが読み取り/書き込みとしてマークされ、コマンドはエラーなしで完了します。
このプロセスは、子供や親が共有ページに書き込むたびに繰り返されます。ページにCoWエラーが発生した後も、通常「その他」プロセスはまだ読み取り専用として表示されます(カーネルの実装によって異なります)。プロセスがここに書き込もうとすると、以前のようにエラーが生成されます。ただし、カーネルはページが共有されなくなったことに気づき、単に読み取り/書き込みとしてマークします。
答え2
現代の実装は1988年にSunOS-4.0で導入されましたmmap()
。すべての最新のオペレーティングシステムは、SunOS-4.0でこの概念を再実装します。その時点から、fork()
呼び出しは通常書き込み中のコピーバリアントでした。
親プロセスが共有方式でマップされたアクセスメモリに書き込むと、そのメモリは子プロセスで共有されたままになります。アクセスプライベートメモリに書き込む場合は、それを変更しようとするとプライベートコピーに置き換えられます。