Googleでこれら4つの違いを見つけようとしていて、これに関する膨大な情報があると予想していましたが、実際には4つの呼び出しの間に明確な比較はありませんでした。
私はこれらのシステムコールの違いを一目でわかるようにコンパイルしようと試みましたが、ここに私が得たものがあります。この情報はすべて正しいですか、重要なものがありませんか?
Fork
:基本的に、fork呼び出しは現在のプロセスの複製を作成します。ほとんどすべての点で同一です(たとえば、一部の実装ではリソースの制限がすべてコピーされるわけではありませんが、可能な限りコピーを作成することをお勧めします)。
新しいプロセス(子)は異なるプロセスID(PID)を取得し、古いプロセス(親)のPIDを親PID(PPID)として保持します。 2つのプロセスはまったく同じコードを実行しているため、forkの戻りコードによってどちらがどちらであるかを判断できます。子は0を取得し、親は子のPIDを取得します。もちろん、これはすべて、フォークコールが機能することを前提としています。動作しない場合、子は作成されず、親はエラーコードを受け取ります。
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)を呼び出したり、致命的なシグナルを受け取った後に明示的に終了することもできます。
入手した情報:
これを読んでくれてありがとう! :)
vfork()
は廃止された最適化です。適切なメモリ管理の前に、fork()
は親のメモリの完全なコピーを作成したため、かなり高価でした。多くの場合、fork()
の後にexec()
が続きます。これにより、現在のメモリマップが破棄され、新しいメモリマップが作成されるため、無駄な費用でした。現在、fork()
はメモリをコピーしません。 「書き込み時にコピー」として設定されるだけなので、fork()
+ exec()
はvfork()
+ exec()
と同じくらい効率的です。
clone()
は、fork()
によって使用されるシステムコールです。いくつかのパラメーターを使用すると、新しいプロセスが作成され、他のパラメーターを使用すると、スレッドが作成されます。それらの違いは、どのデータ構造(メモリ空間、プロセッサの状態、スタック、PID、開いているファイルなど)が共有されているかだけです。
execve()
は、現在の実行可能イメージを、実行可能ファイルからロードされた別のイメージに置き換えます。fork()
は子プロセスを作成します。vfork()
は、fork()
の歴史的に最適化されたバージョンであり、execve()
の直後にfork()
が呼び出されるときに使用されることを意図しています。非MMUシステム(fork()
が効率的に機能できない場合)や、小さなプログラムを実行するために巨大なメモリフットプリントを持つfork()
ingプロセス(JavaのRuntime.exec()
を考えてください)でうまく機能することが判明しました。 POSIXはposix_spawn()
を標準化しており、後者の最近のvfork()
の2つの使用法を置き換えています。posix_spawn()
は、fork()/execve()
と同等の処理を行い、また、間にfdジャグリングを許可します。主に非MMUプラットフォーム用のfork()/execve()
を置き換えることになっています。pthread_create()
は新しいスレッドを作成します。clone()
はLinux固有の呼び出しであり、fork()
からpthread_create()
までのすべてを実装するために使用できます。それは多くの制御を与えます。 rfork()
に触発されました。rfork()
はPlan-9固有の呼び出しです。これは一般的な呼び出しであると想定されており、完全なプロセスとスレッド間で数度の共有が可能です。fork()
-親プロセスの完全なコピーである新しい子プロセスを作成します。子プロセスと親プロセスは異なる仮想アドレス空間を使用しますが、最初は同じメモリページによって設定されます。次に、両方のプロセスが実行されると、オペレーティングシステムがこれら2つのプロセスのいずれかによって書き込まれているメモリページのレイジーコピーを実行し、変更されたページの独立したコピーを割り当てるため、仮想アドレススペースはますます異なり始めます各プロセスのメモリ。この手法は、コピーオンライト(COW)と呼ばれます。vfork()
-新しい子プロセスを作成します。これは、親プロセスの「クイック」コピーです。システムコールfork()
とは対照的に、子プロセスと親プロセスは同じ仮想アドレス空間を共有します。注意!同じ仮想アドレス空間を使用すると、従来のfork()
!の場合のように、親と子の両方が同じスタック、スタックポインター、および命令ポインターを使用します。同じスタックを使用する親と子の間の不要な干渉を防ぐため、親プロセスの実行は、子がexec()
(新しい仮想アドレス空間を作成し、別のスタックへの遷移を作成する)または_exit()
(プロセス実行)。 vfork()
は、「fork-and-exec」モデル用のfork()
の最適化です。 fork()
とは異なり、(_ COWを念頭に置いて)fork()
とは異なり、vfork()
システムコールの実装には、新しいアドレス空間の作成(新しいの割り当てと設定ページディレクトリ)。clone()
-新しい子プロセスを作成します。このシステムコールのさまざまなパラメーターは、親プロセスのどの部分を子プロセスにコピーする必要があるか、どの部分を子プロセス間で共有するかを指定します。その結果、このシステムコールを使用して、スレッドから開始し、完全に独立したプロセスで終了する、あらゆる種類の実行エンティティを作成できます。実際、clone()
システムコールは、pthread_create()
およびfork()
システムコールのすべてのファミリの実装に使用されるベースです。exec()
-プロセスのすべてのメモリをリセットし、指定された実行可能バイナリをロードして解析し、新しいスタックを設定して、ロードされた実行可能ファイルのエントリポイントに制御を渡します。このシステムコールは、呼び出し元に制御を返すことはなく、既存のプロセスに新しいプログラムをロードするのに役立ちます。このシステムコールとfork()
システムコールは、「fork-and-exec」と呼ばれる従来のUNIXプロセス管理モデルを形成します。Fork()、vfork()およびclone()はすべて、do_fork()を呼び出して実際の作業を行いますが、パラメーターは異なります。
asmlinkage int sys_fork(struct pt_regs regs)
{
return do_fork(SIGCHLD, regs.esp, ®s, 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, ®s, 0);
}
asmlinkage int sys_vfork(struct pt_regs regs)
{
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 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;
父親を目覚めさせるexit()およびexecve()によって呼び出されるコード(mm_release()内)は次のとおりです。
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があるためプロセスです)を作成できます。
Fork()とvfork()の違いDifference-between-fork-vfork
Vfork()の動作は、以下のプログラムで詳細に説明されています。
shashi@linuxtechi ~}$ cat vfork_advanced.c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
int n =10;
pid_t pid = vfork(); //creating the child process
if (pid == 0) //if this is a chile process
{
printf("Child process started\n");
}
else//parent process execution
{
printf("Now i am coming back to parent process\n");
}
printf("value of n: %d \n",n); //sample printing to check "n" value
return 0;
}
shashi@linuxtechi ~}$ cc vfork_advanced.c
shashi@linuxtechi ~}$ ./a.out
Child process started
value of n: 10
Now i am coming back to parent process
value of n: 594325573
a.out: cxa_atexit.c:100: __new_exitfn: Assertion `l != NULL' failed.
Aborted
注:繰り返しますが、vforkの結果が定義されていない場合があります。 「n」の値は、10として初めて印刷されましたが、これは予想どおりです。しかし、親プロセスで次回、いくつかのゴミ値を出力しました。