web-dev-qa-db-ja.com

vforkまたはforkの子がexit()ではなく_exit()を呼び出す必要があるのはなぜですか?

vfork()のmanページから:

vfork()は、子がexecve(2)または_exit(2)を呼び出すまで親が一時停止されるという点で、fork()とは異なります。子は、execve()が子によって発行されるまで、スタックを含むすべてのメモリを親と共有します。子は現在の関数から戻ったり、exit()を呼び出したりしてはなりませんが、_exit()を呼び出すことはできます。

なぜ子供は単に_exit()を呼び出すのではなく、exit()を使用する必要があるのですか?これがvfork()fork()の両方に当てはまることを願っています。

12
Sen

前に見た のように、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にも引き継がれます。

3
Bruce Ediger

exitは、atexitによって登録された関数を呼び出すなどの追加のクリーンアップを実行するため、コピーされた部分の外部のデータにアクセスします。 _exitカーネル内を除くクリーンアップなしで、syscallを直接実行します。

3

exit():-I/Oストリームなどを閉じるなどのクリーンアップタスクを実行してから、カーネルに戻ります。 _exit():-カーネルに直接アクセスします(クリーンアップタスクは実行しないでください)。

fork():親と子の両方が異なるファイルテーブルを持っているため、子によって行われた変更は親の環境パラメータに影響を与えません。その逆も同様です。

vfork():親と子の両方が同じファイルテーブルを使用するため、子によって行われた変更は親の環境パラメーターに影響します。例えば一部の変数_var=10_、今度は子によって_var++_を実行してから親を実行すると、親の出力でも_var++_の効果を確認できます。

先ほど言ったように、exit()vfork()を使用すると、すべてのI/Oはすでに閉じられています。そのため、親が正しく実行されていても、すべての変数がフラッシュされ、すべてのストリームが閉じられるため、適切な出力を取得できません。

1
user2670535