もうありません。 COW
というものがあります (コピー オン ライト)、2 つのプロセス (親/子) のいずれかが共有データに書き込もうとした場合にのみ、コピーされます。
過去:
fork()
システム コールは、呼び出しプロセス (親) のアドレス空間をコピーして、新しいプロセス (子) を作成しました。親のアドレス空間を子プロセスにコピーすることは、fork()
の中で最もコストのかかる部分でした。
現在:
fork()
への呼び出し exec()
の呼び出しがすぐに続くことがよくあります。 子プロセスで、子のメモリを新しいプログラムに置き換えます。これは、たとえば、シェルが通常行うことです。この場合、子プロセスは exec()
を呼び出す前にメモリをほとんど使用しないため、親のアドレス空間のコピーに費やされた時間は大幅に無駄になります。 .
このため、後のバージョンの Unix では、仮想メモリ ハードウェアを利用して、プロセスの 1 つが実際にメモリを変更するまで、親と子がそれぞれのアドレス空間にマップされたメモリを共有できるようにしました。この手法は、コピー オン ライトとして知られています。 .これを行うには、fork()
で カーネルは、マップされたページのコンテンツではなく、アドレス空間のマッピングを親から子にコピーし、同時に現在共有されているページを読み取り専用としてマークします。 2 つのプロセスの 1 つがこれらの共有ページの 1 つに書き込もうとすると、プロセスはページ フォールトを取得します。この時点で、Unix カーネルは、ページが実際には「仮想」または「コピー オン ライト」コピーであることを認識するため、障害のあるプロセス用に、ページの新しいプライベートな書き込み可能なコピーを作成します。このように、個々のページの内容は、実際に書き込まれるまで実際にはコピーされません。この最適化により、fork()
が exec()
が続く exec()
を呼び出す前に、子はおそらく 1 ページ (スタックの現在のページ) をコピーするだけで済みます。 .
<ブロック引用>
この組み合わせを (他のソリューションの代わりに) 使用することで得られる利点は何ですか?
何らかの方法で新しいプロセスを作成する必要があります。ユーザー空間プログラムがそれを達成する方法はほとんどありません。 POSIX には vfork()
がありました alognside fork()
、および一部のシステムには、Linux 固有の clone()
などの独自のメカニズムがある場合があります。 、しかし 2008 年以降、POSIX は fork()
のみを指定しています そして posix_spawn()
家族。 fork
+ exec
route はより伝統的で、よく理解されており、欠点がほとんどありません (以下を参照)。 posix_spawn
ファミリは特別な目的として設計されています fork()
が困難な状況での使用に代わるもの;詳細については、その仕様の「根拠」セクションを参照してください。
vfork()
の Linux man ページからの抜粋 明るくなるかもしれません:
Linux では、fork
(2) はコピー オン ライト ページを使用して実装されるため、fork
によって発生する唯一のペナルティ (2) は、親のページ テーブルを複製し、子に固有のタスク構造を作成するために必要な時間とメモリです。 .しかし、昔は fork
(2) 通常は直後に exec
(3) できました。したがって、効率を高めるために、BSD は vfork
を導入しました。 () システム コール。親プロセスのアドレス空間を完全にはコピーしませんでしたが、execve
の呼び出しまで、親プロセスのメモリと制御スレッドを借用しました。 (2) または終了が発生しました。子がそのリソースを使用している間に、親プロセスが中断されました。 vfork
の使用 () はトリッキーでした:たとえば、親プロセスのデータを変更しないことは、どの変数がレジスターに保持されているかを知っていることに依存していました。
(強調を追加)
したがって、廃棄物に関するあなたの懸念は、最新のシステム (Linux に限定されない) では十分に根拠がありませんが、実際には歴史的に問題であり、それを回避するように設計されたメカニズムが実際にありました。最近では、これらのメカニズムのほとんどが廃止されています。
別の回答は次のように述べています:
しかし、昔は fork(2) は呼び出し元のデータ空間の完全なコピーを作成する必要がありましたが、通常はその直後に exec(3) が実行されるため、不必要であることがよくありました.
明らかに、ある人の悪い昔は、他の人が覚えているよりもずっと若いです。
元の UNIX システムには、複数のプロセスを実行するためのメモリがなく、複数のプロセスを同じ論理アドレス空間ですぐに実行できるように物理メモリに保持するための MMU もありませんでした。現在実行中です。
fork システム コールは、戻り値と not を除いて、現在のプロセスをディスクにスワップ アウトするのとほぼ完全に同じでした。 別のプロセスでスワップすることにより、残りのメモリ内コピーを置き換えます。いずれにせよ、子プロセスを実行するために親プロセスを交換する必要があったため、fork+exec はオーバーヘッドを発生させませんでした。
fork+exec が扱いにくい時期があったのは事実です:論理アドレス空間と物理アドレス空間の間のマッピングを提供する MMU があったが、ページ フォールトがコピー オン ライトやその他の多くの仮想アドレス空間に十分な情報を保持していなかった時代です。 - メモリ/デマンド ページング スキームは実行可能でした。
この状況は、UNIX だけでなく、ハードウェアのページ フォールト処理が非常に高速に "再生可能" になるように調整されたほど、十分に苦痛でした。