fork() と vfork() glibc のラッパーは clone() を介して実装されます システムコール。 fork() の関係をよりよく理解するには と clone() 、Linux におけるプロセスとスレッドの関係を考慮する必要があります。
伝統的に、fork() 親プロセスが所有するすべてのリソースを複製し、そのコピーを子プロセスに割り当てます。このアプローチでは、かなりのオーバーヘッドが発生します。子がすぐに exec() を呼び出した場合、すべて無駄になる可能性があります。 . Linux では fork() コピーオンライトを利用 親プロセスと子プロセスの間で共有できるデータのコピーを遅らせるか、完全に回避するページ。したがって、通常の fork() 中に発生する唯一のオーバーヘッドは 親のページ テーブルのコピーと、一意のプロセス記述子構造体 task_struct の割り当てです。 、子供のために。
Linux はまた、スレッドに対して例外的なアプローチを採用しています。 Linux では、スレッドは単なる通常のプロセスであり、たまたま一部のリソースを他のプロセスと共有しています。これは、プロセスとスレッドがまったく異なる種類の獣である Windows や Solaris などの他のオペレーティング システムと比較して、スレッドに対する根本的に異なるアプローチです。 Linux では、各スレッドには通常の task_struct があります。
flags clone() のパラメータ システム コールには、親プロセスと子プロセスが共有する必要があるリソースがある場合、そのリソースを示す一連のフラグが含まれています。プロセスとスレッドは両方とも clone() 経由で作成されます 、唯一の違いは clone() に渡されるフラグのセットです .
通常の fork() 次のように実装できます:
clone(SIGCHLD, 0);
これにより、親とリソースを共有しないタスクが作成され、SIGCHLD を送信するように設定されます。 終了時に親への終了シグナル。
対照的に、アドレス空間、ファイルシステム リソース、ファイル記述子、およびシグナル ハンドラを親と共有するタスク、つまり スレッド 、以下で作成できます:
clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);
vfork() 次に、別の CLONE_VFORK を介して実装されます 子プロセスがシグナルを介してウェイクアップするまで、親プロセスをスリープ状態にします。子は、exec() を呼び出すまで、親の名前空間で実行される唯一のスレッドになります。 または終了します。子はメモリへの書き込みを許可されていません。対応する clone() 呼び出しは次のようになります:
clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0)
sys_clone() の実装 アーキテクチャ固有ですが、作業の大部分は do_fork() で行われます kernel/fork.c で定義 .この関数は静的な clone_process() を呼び出します 、親のコピーとして新しいプロセスを作成しますが、まだ開始しません。 clone_process() レジスタをコピーし、新しいタスクに PID を割り当て、クローン flags によって指定されたプロセス環境の適切な部分を複製または共有します。 . clone_process()のとき リターン、do_clone() 新しく作成されたプロセスを起動し、実行するようにスケジュールします。
Linux でユーザーランド システム コール関数をカーネル システム コールに変換するコンポーネントは、libc です。 GLibC では、NPTL ライブラリがこれを clone(2) にリダイレクトします。 システムコール。