vfork
の目的は、子でexec*
のみを実行したい場合に、プロセスイメージ全体をコピーするオーバーヘッドを排除することでした。 exec*
は子プロセスのイメージ全体を置き換えるため、親のイメージをコピーしても意味がありません。
if ((pid = vfork()) == 0) {
execl(..., NULL); /* after a successful execl the parent should be resumed */
_exit(127); /* terminate the child in case execl fails */
}
他の種類の使用では、vfork
は危険で予測不可能です。
ただし、Linuxを含む現在のほとんどのカーネルでは、vfork
の実装方法が原因で、fork
の主な利点がなくなっています。 fork
が実行されたときにイメージ全体をコピーするのではなく、コピーオンライト技術が使用されます。
すでに述べたように、vfork
のmanページは違いについて明確です。この topic は、fork
、vfork
、clone
およびexec
についての適切な説明を提供します。
以下は、私が使用した一部のLinux 2.6.3x組み込みシステムで経験したfork
とvfork
の見落とされがちな違いです。
コピーオンライト手法を使用しても、親プロセスが使用するメモリを複製するのに十分なメモリがない場合、fork
は失敗します。たとえば、親プロセスが2 GBの常駐メモリ(つまり、使用されていて割り当てられていないメモリ)を使用している場合、空きメモリが2 GB未満になるとfork
は失敗します。単純なプログラムをexec
したいだけで、そのため、巨大な親アドレス空間が必要になることはないので、それはイライラします。
vfork
は、親アドレス空間を複製しないため、このメモリの問題はありません。子プロセスは、親プロセスに害を与えることなくexec*
または_exit
を呼び出すことができるスレッドのように機能します。
メモリページテーブルは複製されないため、vfork
はfork
よりもはるかに高速であり、vfork
の実行時間は、ここで指摘されているように、親プロセスが使用するメモリ量の影響を受けません。 : http://blog.famzah.net/2009/11/20/fork-gets-slower-as-parent-process-use-more-memory/
したがって、パフォーマンスが重要な場合やメモリが限られている場合は、vfork
+ exec*
をfork
+ exec*
の代わりに使用できます。問題は、安全性が低く、manページにvfork
が将来廃止される可能性が高いと記載されていることです。
より安全で移植性の高いソリューションは、より高いレベルでより多くのオプションを提供するposix_spawn
関数を調べることです。渡すオプションに応じて、可能な場合はvfork
を安全に使用します。私はposix_spawn
を正常に使用でき、fork
+ exec
が与えていた厄介な「ダブルメモリチェックの問題」を克服できました。
私のマニュアルページから
(POSIX.1から)vfork()関数はfork(2)と同じ効果がありますが、vfork()によって作成されたプロセスが、タイプpid_tの変数以外のデータを変更した場合の動作は未定義です。 vfork()からの戻り値、vfork()が呼び出された関数からの戻り、または_exit(2)またはexec(3)ファミリーの関数の1つを正常に呼び出す前に他の関数を呼び出します。
vfork()は、子が終了するまで(通常は_exit(2)を呼び出すか、異常に、致命的なシグナルが配信された後)、またはexecve(2 )。その時点まで、子はスタックを含むすべてのメモリを親と共有します。子は現在の関数から戻ったり、exit(3)を呼び出してはなりませんが、_exit(2)を呼び出すことができます。
一部のシステムには、システムコールvfork()があります。これは、元々はfork()のオーバーヘッドの低いバージョンとして設計されました。 fork()はプロセスのアドレス空間全体をコピーする必要があり、そのため非常にコストがかかるため、vfork()関数が導入されました(3.0BSD)。
ただし、vfork()が導入されてから、fork()の実装は大幅に改善されました。特に、「copy-on-write」の導入により、両方のプロセスが参照できるようにすることで、プロセスのアドレス空間のコピーが透過的に偽造されます。それらのいずれかが変更するまで、同じ物理メモリに。これにより、vfork()の正当性が大幅になくなります。実際、システムの大部分はvfork()の元の機能を完全に欠いています。ただし、互換性のために、すべてのvfork()セマンティクスをエミュレートしようとせずに、単にfork()を呼び出すvfork()呼び出しがまだ存在する場合があります。
その結果、fork()とvfork()の違いを実際に利用することは非常に賢明ではありません。実際、理由を正確に理解していない限り、vfork()を使用することはおそらく賢明ではありません。
2つの基本的な違いは、vfork()を使用して新しいプロセスが作成されると、親プロセスが一時的に中断され、子プロセスが親のアドレス空間を借用する可能性があることです。この奇妙な状況は、子プロセスが終了するか、execve()を呼び出すまで続き、その時点で親プロセスは続行します。
つまり、vfork()の子プロセスは、親プロセスの変数を予期せず変更しないように注意する必要があります。特に、子プロセスはvfork()呼び出しを含む関数から戻ってはならず、exit()を呼び出してはなりません(終了する必要がある場合は、_exit()を使用する必要があります);実際、これは子にも当てはまります。通常のfork()の場合)。
2つの基本的な違いは、vfork()
を使用して新しいプロセスが作成されると、親プロセスが一時的に中断され、子プロセスが親のアドレススペースを借用する可能性があることです。この奇妙な状況が続く子プロセスが終了するか、execve()
を呼び出すまで、その時点で親プロセスは続行します。
つまり、vfork()
の子プロセスは、親プロセスの変数を予期せず変更しないように注意する必要があります。特に、子プロセスは、vfork()
呼び出しを含む関数から復帰してはならず、exit()
を呼び出してはなりません(終了する必要がある場合、実際には_exit();
を使用する必要があります) 、これは通常の子にも当てはまりますfork()
)。
ただし、vfork()
が導入されてから、fork()
の実装は大幅に改善されました。特に、「copy-on-write」の導入によりプロセスアドレスのコピースペースは、どちらかのプロセスが変更するまで、両方のプロセスが同じ物理メモリを参照できるようにすることで、透過的に偽造されます。これにより、vfork();
の正当性が大幅に排除されます。 vfork()
完全に。ただし、互換性のために、vfork()
セマンティクスのすべてをエミュレートしようとせずに、単にfork()
を呼び出すvfork()
呼び出しがまだ存在する場合があります。
その結果、fork()
とvfork()
の違いを実際に利用することは非常に賢明ではありません。実際、なぜそうしたいのか正確に知らない限り、vfork()
を使用することはおそらく賢明ではありません。