vfork()
のmanページから:
vfork()は、子がexecve(2)または_exit(2)を呼び出すまで親が一時停止されるという点で、fork()とは異なります。子は、execve()が子によって発行されるまで、スタックを含むすべてのメモリを親と共有します。子は現在の関数から戻ったり、exit()を呼び出したりしてはなりませんが、_exit()を呼び出すことはできます。
なぜ子供は単に_exit()
を呼び出すのではなく、exit()
を使用する必要があるのですか?これがvfork()
とfork()
の両方に当てはまることを願っています。
前に見た のように、vfork
は、子プロセスが親のメモリにアクセスすることを許可しません。 exit
はCライブラリ関数です(そのため、 多くの場合 as exit(3)
)。 Cストリーム(_stdio.h
_で宣言された関数を介して開かれるファイル)のフラッシュとクローズ、atexit
に登録されたユーザー指定関数の実行などのさまざまなクリーンアップタスクを実行します。これらすべてのタスクには、プロセスメモリの読み取りと書き込みが含まれます。
__exit
_ クリーンアップせずに終了します。これは直接システムコールであり(これが_exit(2)
として記述される理由です)、通常はシステムコール番号をプロセッサレジスタに配置し、特定のプロセッサ命令を実行することによって実装されます(システムコールハンドラに分岐します)。これはプロセスメモリにアクセスする必要がないため、vfork
の後に安全にアクセスできます。
fork
以降は、そのような制限はありません。親プロセスと子プロセスは完全に自律的になりました。
子プロセスが終了したときにstdio(またはその他の)バッファーがフラッシュされないようにするために、子は_exit()を呼び出します。子プロセスは親プロセスの正確なコピーを構成するため、子プロセスには、親が「stdout」または「stderr」に持っていたもの、つまり<stdio.h>からのバッファーがまだあります。親プロセスのバッファーがいっぱいになり、フラッシュされると、exit()を呼び出して1つは子プロセスのatexitハンドラーから、もう1つは親プロセスから呼び出すことで、二重の出力を取得できます(不都合な場合もあります)。
上記の回答はstdio.hの詳細に集中していると思いますが、上記の回答の1つが示すように、そのアイデアはおそらく他のバッファリングされたI/Oにも引き継がれます。
exit
は、atexit
によって登録された関数を呼び出すなどの追加のクリーンアップを実行するため、コピーされた部分の外部のデータにアクセスします。 _exit
カーネル内を除くクリーンアップなしで、syscallを直接実行します。
exit()
:-I/Oストリームなどを閉じるなどのクリーンアップタスクを実行してから、カーネルに戻ります。 _exit()
:-カーネルに直接アクセスします(クリーンアップタスクは実行しないでください)。
fork()
:親と子の両方が異なるファイルテーブルを持っているため、子によって行われた変更は親の環境パラメータに影響を与えません。その逆も同様です。
vfork()
:親と子の両方が同じファイルテーブルを使用するため、子によって行われた変更は親の環境パラメーターに影響します。例えば一部の変数_var=10
_、今度は子によって_var++
_を実行してから親を実行すると、親の出力でも_var++
_の効果を確認できます。
先ほど言ったように、exit()
でvfork()
を使用すると、すべてのI/Oはすでに閉じられています。そのため、親が正しく実行されていても、すべての変数がフラッシュされ、すべてのストリームが閉じられるため、適切な出力を取得できません。