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


11

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

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

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

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

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

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

私はLinuxを優先します。

ありがとう。

回答:


15

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

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

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

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

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

ただし、何をしているのか本当に理解していない限り、またはを使用しないでください。古き良きに固執して。vfork()posix_spawn()fork()exec()

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

ただし、古き良き時代にfork(2) は、呼び出し側のデータスペースの完全なコピーを作成する必要exec(3)がありました。したがって、BSDは効率を高めるためvfork() に、親プロセスのアドレス空間を完全にはコピーしませんでしたが、呼び出しexecve(2)または終了が発生するまで親のメモリと制御のスレッドを借用したシステムコールを導入しました。子がリソースを使用している間、親プロセスが一時停止されました。の使用にvfork()は注意が必要でし た。たとえば、親プロセスでデータを変更しないことは、どの変数がレジスターに保持されているかを知ることに依存していました。


ありがとう。「exec()は新しいプログラムに新しいアドレス空間を提供します。」プログラムをプロセスのアドレス空間にロードするためのexec()の通常の動作ですか?2つのリンクで、通常または特にvfork()用に新しいアドレス空間を作成する場所は見つかりませんでした。
ティム

1
おかしいのは、vfork()が他のほとんどすべてに対して勝っていることです。ギガバイトの書き込み可能なメモリがある場合、fork()よりも途方もなく高速です。
ジョシュア

2
使用するように言わないでくださいposix_spawn。それはかなり困難使って正しいコードを書くことですposix_spawn昔ながらよりもfork、あなたがしようとした場合、あなたはあなたが間に行わ必要がある事ないファイルアクションまたは属性が存在しないというレンガの壁にぶつかることforkとしますexec。また、vforkのような効率が保証されるわけではないため、人々が解決したいと思っている問題も解決されません。
zwol 2018年

1
@zwol:それは本当に悪いアドバイスです。posix_spawn必要な機能が不足している可能性がありますが(Cまたはinline-on-cmdlineシェルスクリプトで記述された中間ヘルパープログラムを使用してこれを解決できます)、希望する結果を達成しようとすると、vfork危険な未定義の動作が呼び出されます。の仕様でvforkは、ランダム関数を呼び出して子がbeforeを継承する状態を設定することを許可していませんexecve。そうしようとすると、親の状態が破損する可能性があります。
R .. GitHub ICEのヘルプを停止する

1
@ジョシュア:の最新の実装はposix_spawnvforkほとんどの状況とほぼ同じように動作します。違いがあるのvforkは、非常に危険なケースposix_spawnです。実行前に子で実行されないようにする必要があるシグナルハンドラがインストールされている場合です。
R .. GitHub ICEのヘルプを停止する

4

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

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

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

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

  • 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()ksh93Bourne Shellcsh

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

ところで:の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
GNU Cライブラリもmusl Cライブラリもposix_spawn()Linuxの上には実装されていませんvfork()。彼らは両方ともそれを上に実装し__clone()ます。
JdeBP 2018年

1
@JdeBP:あなたはvfork()ただ電話がclone()正しいことを知っていますか?それは文字通りカーネルのワンライナーです。
ジョシュア

1
「重要:Linuxでは、実際のvfork()実装はありません。」<-これは真実ではなく、少なくとも10年間真実ではありません。シェルのベンチマークでLinux vforkとのパフォーマンスの違いが観察されないfork場合は、何か問題があります。
zwol 2018年

1
「重要:Linuxでは実際のvfork()実装がない」で始まるこの回答の後半は、ほとんどまたは完全に間違っています。
R .. GitHub ICEのヘルプを停止する

1
確認せずにクレームしないでください。現在のBourneシェルはvforkサポートの有無にかかわらずコンパイルできるため、Linuxのデバッグ機能では信頼できる結果が得られないと思われる場合でも、シェルでvforkを使用した場合と使用した場合の構成呼び出しの実行時間を比較できます。800テストでconfigureスクリプトを使用しています。Solarisでは、vforkを使用するBourne Shellは、forkの場合と比較して、合計で30%少ないシステムCPU時間を必要とします。Linuxでは、同じテストの結果、システムCPU時間は10%未満短縮されます。Solarisでは、多くのコンパイラコールが含まれているため、3倍ではありません。
schily
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.