web-dev-qa-db-ja.com

子プロセスが作成直後にexec()またはexit()を呼び出すときにvfork()が使用されるのはなぜですか?

オペレーティングシステムの概念とAPUEは言う

Vfork()を使用すると、親プロセスは一時停止され、子プロセスは親のアドレス空間を使用します。 vfork()はコピーオンライトを使用しないため、子プロセスが親のアドレススペースのページを変更した場合、再開すると、変更されたページが親に表示されます。したがって、子プロセスが親のアドレス空間を変更しないように注意してvfork()を使用する必要があります。

vfork()は、子プロセスが作成直後にexec()またはexit()を呼び出すときに使用されることを意図しています。

最後の文をどのように理解できますか?

vfork()によって作成された子プロセスがexec()を呼び出すとき、新しいプログラムをロードすることによって、exec()が親プロセスのアドレス空間を変更しませんか?

vfork()によって作成された子プロセスがexit()を呼び出すとき、exit()は子を終了するときに親プロセスのアドレス空間を変更しませんか?

私はLinuxを好みます。

ありがとう。

11
Tim

vfork()によって作成された子プロセスがexec()を呼び出すとき、新しいプログラムをロードすることによって、exec()が親プロセスのアドレス空間を変更しませんか?

いいえ、exec()は新しいプログラムに新しいアドレス空間を提供します。親アドレススペースは変更されません。たとえば POSIXのexec関数の説明Linux execve()マンページ を参照してください。

Vfork()によって作成された子プロセスがexit()を呼び出すとき、exit()は子を終了するときに親プロセスのアドレス空間を変更しませんか?

プレーンexit() may –実行中のプログラム(ライブラリを含む)によってインストールされた終了フックを実行します。 vfork()はより制限的です。したがって、Linuxでは、 it mandates_exit() の使用で、Cを呼び出さないライブラリのクリーンアップ関数。

vfork()を正しく実行するのは非常に難しいことがわかりました。現在のバージョンのPOSIX標準では削除されており、代わりに posix_spawn() を使用する必要があります。

ただし、本当にあなたが何をしているのかを知っていない限り、あなたはしないのどちらかを使用する必要がありますvfork()またはposix_spawn();古き良きfork()exec()に固執する。

上にリンクされているLinuxマンページは、より多くのコンテキストを提供します。

ただし、古き良き時代には、通常fork(2)の直後にexec(3)が実行されるため、vfork()は呼び出し側のデータスペースの完全なコピーを作成する必要があります。したがって、効率を高めるために、BSDはexecve(2)システムコールを導入しました。これは、親プロセスのアドレススペースを完全にはコピーしませんでしたが、vfork()の使用はトリッキーでした。たとえば、親プロセスでデータを変更しないことは、どの変数がレジスターに保持されているかを知ることに依存していました。

15
Stephen Kitt

vfork()を呼び出すと、新しいプロセスが作成され、その新しいプロセスはスタックを除いて親プロセスのプロセスイメージを借用します。子プロセスには独自の新しいスタックスターが与えられますが、vfork()を呼び出した関数からのreturnは許可されません。

子が実行されている間、子は親のアドレス空間を借用したため、親プロセスはブロックされます。

何をするかに関係なく、スタックにアクセスするすべてのものは子のプライベートスタックのみを変更します。ただし、グローバルデータを変更すると、共通データが変更されるため、親にも影響します。

グローバルデータを変更するものは次のとおりです。

  • malloc()またはfree()の呼び出し

  • stdioを使用する

  • 信号設定の変更

  • vfork()を呼び出した関数に対してローカルではない変数を変更する。

  • ...

_exit()を呼び出すと(重要です。決してexit()を呼び出さないでください)、子は終了し、制御は親に戻されます。

exec*()ファミリーの関数を呼び出すと、新しいプログラムコード、新しいデータ、および親からのスタックの一部を使用して、新しいアドレススペースが作成されます(以下を参照)。これが準備できたら、子は子からアドレススペースを借用せず、独自のアドレススペースを使用します。

コントロールは親に戻されます。これは、そのアドレススペースが別のプロセスで使用されなくなったためです。

重要:Linuxでは、実際のvfork()実装はありません。 Linuxは、1988年にSunOS-4.0で導入されたコピーオンライトvfork()の概念に基づいてfork()を実装しています。ユーザーがvfork()を使用しているとユーザーに信じさせるために、Linux子が_exit()またはexec*()関数の1つを呼び出さなかったときに、共有データをセットアップして親を一時停止するだけです。

したがって、Linuxは、実際のvfork()がカーネル内の子のアドレス空間記述をセットアップする必要がないという事実から恩恵を受けません。これにより、vfork()よりも速くないfork()が生成されます。実際のvfork()を実装するシステムでは、通常fork()より3倍高速であり、vfork()を使用するシェルのパフォーマンスに影響します-_ksh93_、最近の_Bourne Shell_およびcsh

exit() edの子からvfork()を呼び出すべきではない理由は、exit()を呼び出す前にフラッシュされていないデータがある場合に備えて、vfork()がstdioをフラッシュするためです。 )] _。これは奇妙な結果を引き起こす可能性があります。

ところで:posix_spawn()vfork()の上に実装されているため、vfork()はOSから削除されません。 Linuxはvfork()に対してposix_spawn()を使用しないと述べられています。

スタックについては、ドキュメントがほとんどありません。Solarisのマニュアルページに次のように記載されています。

_ The vfork() and vforkx() functions can normally be used  the
 same  way  as  fork() and forkx(), respectively. The calling
 procedure, however, should not return while running  in  the
 child's  context,  since the eventual return from vfork() or
 vforkx() in the parent would be to a  stack  frame  that  no
 longer  exists. 
_

したがって、実装は好きなように実行できます。 Solarisの実装では、vfork()を呼び出す関数のスタックフレームに共有メモリを使用します。親からスタックの古い部分へのアクセスを許可する実装はありません。

4
schily