fork()、vfork()、exec()およびclone()の違い


197

私はGoogleでこれら4つの違いを見つけようとしていたので、これについては膨大な量の情報があると予想していましたが、4つの呼び出しの間に明確な比較はありませんでした。

私は、これらのシステムコールの違いを一目で確認できるような種類のコンパイルを試みることに着手しました。これが私が得たものです。この情報はすべて正しいですか/重要な情報が欠けていますか?

Fork :fork呼び出しは基本的に現在のプロセスの複製を作成し、ほぼすべての点で同一です(たとえば、一部の実装ではリソースの制限がすべてコピーされるわけではありませんが、できるだけ近いコピーを作成するという考えです)。

新しいプロセス(子)は別のプロセスID(PID)を取得し、古いプロセス(親)のPIDをその親PID(PPID)として持ちます。2つのプロセスがまったく同じコードを実行しているので、フォークの戻りコードによってどちらがどちらであるかがわかります。子は0を取得し、親は子のPIDを取得します。もちろん、これはすべてfork呼び出しが機能することを前提としています。機能しない場合、子は作成されず、親はエラーコードを受け取ります。

Vfork:vforkとforkの基本的な違いは、vfork()で新しいプロセスが作成されると、親プロセスが一時的に中断され、子プロセスが親のアドレス空間を借用する可能性があることです。この奇妙な状況は、子プロセスが終了するか、execve()を呼び出すまで続き、その時点で親プロセスは続行します。

つまり、vfork()の子プロセスは、親プロセスの変数を予期せず変更しないように注意する必要があります。特に、子プロセスはvfork()呼び出しを含む関数から戻ってはならず、exit()を呼び出してはなりません(終了する必要がある場合は、_exit()を使用する必要があります);実際、これは子にも当てはまります。通常のfork()の場合)。

Exec :exec呼び出しは、基本的に現在のプロセス全体を新しいプログラムに置き換える方法です。プログラムを現在のプロセス空間にロードし、エントリポイントから実行します。exec()は、現在のプロセスを、関数が指す実行可能ファイルで置き換えます。exec()エラーがない限り、制御が元のプログラムに戻ることはありません。

Clone :クローンは、フォークとして、新しいプロセスを作成します。forkとは異なり、これらの呼び出しにより、子プロセスは、メモリスペース、ファイル記述子のテーブル、シグナルハンドラーのテーブルなど、その実行コンテキストの一部を呼び出しプロセスと共有できます。

子プロセスがcloneで作成されると、関数アプリケーションfn(arg)が実行されます。(これは、元のfork呼び出しのポイントから子で実行が継続されるforkとは異なります。)fn引数は、実行の開始時に子プロセスによって呼び出される関数へのポインターです。arg引数はfn関数に渡されます。

fn(arg)関数アプリケーションが戻ると、子プロセスは終了します。fnによって返される整数は、子プロセスの終了コードです。子プロセスは、exit(2)を呼び出すことによって、または致命的なシグナルを受信した後で、明示的に終了することもできます。

取得した情報:

これを読むために時間を割いてくれてありがとう!:)


2
vforkがexit()を呼び出さないのはなぜですか?または戻りませんか?exit()は_exit()を使用しないのですか?私も理解しようとしています:)
LazerSharks '20 / 07/20

2
@Gnuey:潜在的に(fork()それがLinuxであり、おそらくすべてのBSDで実装されている場合とは異なる方法で実装されている場合)、親のアドレス空間を借用しているため。execve()または以外にも_exit()、親が混乱する可能性が非常に高くなります。特に、exit()呼び出し、atexit()ハンドラやその他の「ファイナライザ」、例えば:それは標準入出力ストリームをフラッシュします。vfork()子から戻ると、親のスタックが混乱する可能性があります(以前と同じ警告)。
ninjalj 14

親プロセスのスレッドはどうなるのだろうと思っていました。それらはすべて複製されていforkますか、それともsyscall を呼び出すスレッドのみですか?
Mohammad Jafar Mashhadi 2014年

@LazerSharks vforkは、コピーオンライト保護なしでメモリが共有されるスレッドのようなプロセスを生成するため、スタックスタッフを実行すると、親プロセスが破棄される可能性があります。
Jasen

回答:


159
  • vfork()廃止された最適化です。適切なメモリ管理の前fork()に、親のメモリの完全なコピーを作成したため、かなり高価でした。多くの場合、aのfork()後にが続きexec()、現在のメモリマップを破棄して新しいメモリマップを作成するため、不要な費用がかかりました。今日、fork()メモリをコピーしません。それは単に「コピーオンライト」として設定し、それではfork()+は、exec()同じように効率的なようですvfork()+ exec()

  • clone()が使用するsyscall fork()です。いくつかのパラメーターで新しいプロセスを作成し、他のパラメーターでスレッドを作成します。それらの違いは、どのデータ構造(メモリ空間、プロセッサの状態、スタック、PID、開いているファイルなど)が共有されるかだけです。



22
vfork実行できるようにするために一時的にはるかに多くのメモリをコミットする必要がなくなり、程度が高くなくてexecfork、より効率的です。したがって、ハンキングする大きなプログラムが子プロセスを生成できるように、メモリをオーバーコミットする必要を回避できます。したがって、パフォーマンスを向上させるだけでなく、実現可能にすることも可能です。
Deduplicator

5
実際、RSSが大きい場合に、fork()が安価であることを実感しました。これは、カーネルがまだすべてのページテーブルをコピーする必要があるためだと思います。
マルティナフェラーリ

4
すべてのページテーブルをコピーし、両方のプロセスですべての書き込み可能なメモリのコピーオンライトを設定し、TLBをフラッシュしてから、すべての変更を親に戻す(そしてTLBを再度フラッシュする)必要がありexecます。
zwol 2016

3
vforkは、cygwin(MicrosoftのWindows上で実行されるdllをエミュレートするカーネル)でも引き続き役立ちます。cygwinは、基盤となるOSにフォークがないため、効率的なフォークを実装できません。
ctrl-alt-delor

80
  • execve() 現在の実行可能イメージを、実行可能ファイルからロードされた別のイメージに置き換えます。
  • fork() 子プロセスを作成します。
  • vfork()はの歴史的に最適化されたバージョンでfork()execve()がの直後に呼び出されたときに使用されることを意図していますfork()。それは、非MMUシステム(fork()効率的な方法では機能しない)でうまく機能することが判明しfork()、巨大なメモリフットプリントでプロセスを実行していくつかの小さなプログラム(Javaを考えるRuntime.exec())を実行するときに判明しました。POSIXは、posix_spawn()これらの後者の2つの最近の使用法を置き換えるために標準化しましたvfork()
  • posix_spawn()と同等の処理を行い、fork()/execve()その間にいくつかのfdジャグリングを許可します。これはfork()/execve()、主に非MMUプラットフォームで、を置き換えることになっています。
  • pthread_create() 新しいスレッドを作成します。
  • clone()Linux固有の呼び出しであり、からfork()までのすべてを実装するために使用できますpthread_create()。それは多くの制御を与えます。に触発されましたrfork()
  • rfork()プラン9固有の呼び出しです。これは、完全なプロセスとスレッド間で、ある程度の共有を可能にする一般的な呼び出しであると想定されています。

2
実際に要求されたよりも多くの情報を追加してくれてありがとう、それは私が時間を節約するのに役立ちました
Neeraj 2013年

5
プラン9はそのようないじめです。
JJ

1
MMUの意味を思い出せない人のために:「メモリ管理ユニット」- ウィキペディアで
mgarey

43
  1. fork()-親プロセスの完全なコピーである新しい子プロセスを作成します。子プロセスと親プロセスは異なる仮想アドレス空間を使用しますが、最初は同じメモリページが読み込まれます。次に、両方のプロセスが実行されると、仮想アドレススペースがますます異なります。オペレーティングシステムがこれらの2つのプロセスのいずれかによって書き込まれているメモリページのレイジーコピーを実行し、変更されたページの独立したコピーを割り当てるためです。各プロセスのメモリ。この手法は、コピーオンライト(COW)と呼ばれます。
  2. vfork()-新しい子プロセスを作成します。これは、親プロセスの「クイック」コピーです。システムコールとは対照的にfork()、子プロセスと親プロセスは同じ仮想アドレス空間を共有します。注意!クラシックの場合と同様に、同じ仮想アドレス空間を使用して、親と子の両方が同じスタック、スタックポインター、および命令ポインターを使用しますfork()。同じスタックを使用する親と子の間の不要な干渉を防ぐため、親プロセスの実行は、子がexec()(新しい仮想アドレス空間を作成して別のスタックに移行する)または_exit()(プロセス実行の終了)を呼び出すまでフリーズされます。)。「fork-and-exec」モデルvfork()の最適化ですfork()。とはfork()異なり、4〜5倍の速度で実行できます。fork()(COWを考慮しても)、vfork()システムコールの実装には、新しいアドレススペースの作成(新しいページディレクトリの割り当てと設定)は含まれません。
  3. clone()-新しい子プロセスを作成します。このシステムコールのさまざまなパラメーターは、親プロセスのどの部分を子プロセスにコピーする必要があるか、およびそれらの間で共有する部分を指定します。その結果、このシステムコールを使用して、スレッドから開始し、完全に独立したプロセスで終了する、あらゆる種類の実行エンティティを作成できます。実際、clone()システムコールは、システムコールのpthread_create()すべてのファミリの実装に使用されるベースfork()です。
  4. exec()-プロセスのすべてのメモリをリセットし、指定された実行可能バイナリをロードして解析し、新しいスタックをセットアップして、ロードされた実行可能ファイルのエントリポイントに制御を渡します。このシステムコールは、呼び出し元に制御を返すことはなく、既存のプロセスに新しいプログラムをロードする役割を果たします。このシステムコールとfork()システムコールを組み合わせると、「fork-and-exec」と呼ばれる従来のUNIXプロセス管理モデルが形成されます。

2
のBSDおよびPOSIX要件vforkは非常に弱いためvfork、同義語にすることは合法ですfork(そしてPOSIX.1-2008はvfork仕様から完全に削除されます)。同義語のシステムでコードをテストした場合(たとえば、NetBSD以外のほとんどの4.4以降のBSD、2.2.0より前のLinuxカーネルなど)、vfork契約に違反していても爆発する可能性があります。他の場所で実行した場合。それをシミュレートするものfork(OpenBSDなど)でも、子がexecsになるまで親が実行を再開しないことを保証し_exitます。途方もなくポータブルではありません。
ShadowRanger

2
3番目のポイントの最後の文について:Linuxでstraceを使用していることに気付きました。確かにfork()のglibcラッパーがクローンsyscallを呼び出しているのに対し、vfork()のラッパーはvfork syscallを呼び出しています
ilstam

7

fork()、vfork()およびclone()はすべて、実際の作業を行うためにdo_fork()を呼び出しますが、パラメーターは異なります。

asmlinkage int sys_fork(struct pt_regs regs)
{
    return do_fork(SIGCHLD, regs.esp, &regs, 0);
}

asmlinkage int sys_clone(struct pt_regs regs)
{
    unsigned long clone_flags;
    unsigned long newsp;

    clone_flags = regs.ebx;
    newsp = regs.ecx;
    if (!newsp)
        newsp = regs.esp;
    return do_fork(clone_flags, newsp, &regs, 0);
}
asmlinkage int sys_vfork(struct pt_regs regs)
{
    return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 0);
}
#define CLONE_VFORK 0x00004000  /* set if the parent wants the child to wake it up on mm_release */
#define CLONE_VM    0x00000100  /* set if VM shared between processes */

SIGCHLD means the child should send this signal to its father when exit.

forkの場合、子と父親は独立したVMページテーブルを持っていますが、効率性のため、forkは実際にはページをコピーせず、すべての書き込み可能なページを子プロセスの読み取り専用に設定します。したがって、子プロセスがそのページに何かを書き込みたい場合、ページ例外が発生し、カーネルは古いページから複製された新しいページを書き込み権限で割り当てます。それは「コピーオンライト」と呼ばれています。

vforkの場合、仮想メモリは子と父によって正確に作成されます。そのため、父と子は互いに影響を与えるため、同時に起きることはできません。したがって、父親は "do_fork()"の最後でスリープ状態になり、子がexit()またはexecve()を呼び出すと、新しいページテーブルを所有するようになるため、目覚めます。これは、父親が眠るコード(do_fork()内)です。

if ((clone_flags & CLONE_VFORK) && (retval > 0))
down(&sem);
return retval;

以下は、父親を起こさせるコード(mm_release()で、exit()およびexecve()によって呼び出される)です。

up(tsk->p_opptr->vfork_sem);

sys_clone()の場合、任意のclone_flagsを入力できるため、より柔軟です。したがって、pthread_create()はこのシステムコールを多くのclone_flagsで呼び出します。

int clone_flags =(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM);

概要:fork()、vfork()およびclone()は、親プロセスと共有リソースのマウントが異なる子プロセスを作成します。また、vfork()とclone()は、親プロセスとVMページテーブルを共有しているため、スレッド(実際には独立したtask_structがあるためプロセスです)を作成できるとも言えます。


-4

fork()では、子プロセスまたは親プロセスのいずれかがCPUの選択に基づいて実行されます。しかし、vfork()では、確実に子プロセスが最初に実行されます。子が終了した後、親が実行されます。


3
違う。vfork()として実装できfork()ます。
ninjalj 2013

AnyFork()の後、誰が最初の親/子を実行するかは定義されていません。
AjayKumarBasuthkar 2014年

5
@Raj:分岐後にシリアル順序の暗黙の概念があると考えると、いくつかの概念的な誤解があります。Forkは新しいプロセスを作成し、両方のプロセスに制御を返します(それぞれが異なるを返しますpid)。オペレーティングシステムは、そのようなことが理にかなっている場合(たとえば、複数のプロセッサ)に、新しいプロセスを並行して実行するようにスケジュールできます。何らかの理由でこれらのプロセスを特定の順番で実行する必要がある場合は、フォークでは提供されない追加の同期が必要です。率直に言って、そもそもフォークは欲しくないでしょう。
Andon M. Coleman

実際、@ AjayKumarBasuthkarと@ninjaljは、どちらも間違っています。でvfork()、子が最初に実行されます。マニュアルページにあります。親の処刑は、子供が死ぬか、死ぬかするまで中断されexecます。そして、ninjaljはカーネルのソースコードを調べます。実装する方法はありませんvfork()ようにfork()彼らが異なる引数を渡すので、do_fork()カーネル内。ただし、syscallを使用vforkして実装できcloneます
Zac Wimer

@ZacWimer:別の答えにShadowRangerさんのコメントを参照stackoverflow.com/questions/4856255/... 旧Linuxはなかったとして、明らかに(非MMUの多くのシステムに移植される傾向がある)のNetBSD以外のBSDが行う、それらをsynonimize。Linuxマンページから:4.4BSDではfork(2)の同義語になりましたが、NetBSDでは再び導入されました。⟨netbsd.org/Documentation/kernel/vfork.html seeを参照してください。Linuxでは、2.2.0-pre6まではfork(2)と同等でした。
ninjalj
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.