Linuxの多くのプログラムとmanページで、fork()
を使用したコードを見てきました。なぜfork()
を使用する必要があり、その目的は何ですか?
fork()
は、Unixで新しいプロセスを作成する方法です。 fork
を呼び出すと、独自の アドレス空間 を持つ独自のプロセスのコピーを作成しています。これにより、複数のタスクが互いに独立して実行され、あたかもそれぞれがマシンのフルメモリを持っているかのように実行できます。
fork
の使用例は次のとおりです。
fork
を使用して、コマンドラインから呼び出すプログラムを実行します。fork
を使用して複数のサーバープロセスを作成し、それぞれが独自のアドレス空間でリクエストを処理します。 1つが死んだりメモリをリークしたりしても、他は影響を受けないため、フォールトトレランスのメカニズムとして機能します。fork
を使用して、個別のプロセス内で各ページを処理します。これにより、1ページのクライアント側コードがブラウザ全体をダウンさせるのを防ぎます。fork
は、一部の並列プログラム( [〜#〜] mpi [〜#〜] を使用して記述されたプログラムなど)でプロセスを生成するために使用されます。これは、 threads を使用する場合とは異なることに注意してください。これは、独自のアドレス空間を持たず、withinプロセスです。fork
を間接的に使用して子プロセスを開始します。たとえば、Pythonで _subprocess.Popen
_ などのコマンドを使用するたびに、fork
は子プロセスであり、その出力を読み取ります。これにより、プログラムを連携させることができます。シェルでのfork
の一般的な使用法は次のようになります。
_int child_process_id = fork();
if (child_process_id) {
// Fork returns a valid pid in the parent process. Parent executes this.
// wait for the child process to complete
waitpid(child_process_id, ...); // omitted extra args for brevity
// child process finished!
} else {
// Fork returns 0 in the child process. Child executes this.
// new argv array for the child process
const char *argv[] = {"arg1", "arg2", "arg3", NULL};
// now start executing some other program
exec("/path/to/a/program", argv);
}
_
シェルは、exec
を使用して子プロセスを生成し、完了するまで待機してから、独自の実行を続行します。この方法でforkを使用する必要がないことに注意してください。並列プログラムが行うように、多くの子プロセスをいつでも生成でき、それぞれがプログラムを同時に実行できます。基本的に、Unixシステムで新しいプロセスを作成するときは常に、fork()
を使用しています。 Windowsの同等の機能については、 CreateProcess
をご覧ください。
より多くの例とより長い説明が必要な場合は、 Wikipedia にまともな要約があります。そして、 ここにいくつかのスライドがあります ここでは、最新のオペレーティングシステムでのプロセス、スレッド、および並行性の動作について説明しています。
fork()は、Unixが新しいプロセスを作成する方法です。 fork()を呼び出した時点で、プロセスが複製され、2つの異なるプロセスがそこから実行を継続します。それらの1つである子は、fork()が0を返します。もう1つである親は、fork()が子のPID(プロセスID)を返します。
たとえば、シェルで次のように入力すると、シェルプログラムはfork()を呼び出し、渡されたコマンド(この場合はtelnetd)を子で実行し、親は再びプロンプトを表示します。バックグラウンドプロセスのPIDを示すメッセージとして。
$ telnetd &
新しいプロセスを作成する理由については、オペレーティングシステムが同時に多くのことを行う方法です。プログラムを実行し、実行中に別のウィンドウに切り替えて別の操作を実行できるのは、このためです。
fork()は、子プロセスを作成するために使用されます。 fork()関数が呼び出されると、新しいプロセスが生成され、fork()関数呼び出しは子と親に対して異なる値を返します。
戻り値が0の場合、自分が子プロセスであることがわかり、戻り値が数値(子プロセスIDである場合)である場合、自分が親であることがわかります。 (そして、それが負の数である場合、フォークは失敗し、子プロセスは作成されませんでした)
fork()は、親と同一の新しい子プロセスを作成します。そのため、その後のコードで実行するすべてが両方のプロセスで実行されます。たとえば、サーバーがあり、複数のリクエストを処理する場合に非常に便利です。
fork()は、基本的に、この関数を呼び出しているプロセスの子プロセスを作成するために使用されます。 fork()を呼び出すたびに、子IDにゼロが返されます。
pid=fork()
if pid==0
//this is the child process
else if pid!=0
//this is the parent process
これにより、親と子に異なるアクションを提供し、マルチスレッド機能を利用できます。
システムコールfork()は、プロセスを作成するために使用されます。引数を取らず、プロセスIDを返します。 fork()の目的は、呼び出し元の子プロセスになる新しいプロセスを作成することです。新しい子プロセスが作成された後、両方のプロセスはfork()システムコールに続く次の命令を実行します。したがって、親と子を区別する必要があります。これは、fork()の戻り値をテストすることで実行できます。
Fork()が負の値を返す場合、子プロセスの作成は失敗しました。 fork()は、新しく作成された子プロセスにゼロを返します。 fork()は、子プロセスのプロセスIDである正の値を親に返します。返されるプロセスIDは、sys/types.hで定義されているpid_t型です。通常、プロセスIDは整数です。さらに、プロセスは関数getpid()を使用して、このプロセスに割り当てられたプロセスIDを取得できます。そのため、システムがfork()を呼び出した後、簡単なテストでどのプロセスが子であるかを知ることができます。 Unixは親のアドレス空間の正確なコピーを作成し、それを子に渡すことに注意してください。したがって、親プロセスと子プロセスには別々のアドレススペースがあります。
上記の点を明確にするための例を使って理解しましょう。この例では、親プロセスと子プロセスを区別しません。
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#define MAX_COUNT 200
#define BUF_SIZE 100
void main(void)
{
pid_t pid;
int i;
char buf[BUF_SIZE];
fork();
pid = getpid();
for (i = 1; i <= MAX_COUNT; i++) {
sprintf(buf, "This line is from pid %d, value = %d\n", pid, i);
write(1, buf, strlen(buf));
}
}
上記のプログラムがfork()の呼び出しの時点まで実行されると仮定します。
Fork()の呼び出しが正常に実行されると、Unixは2つの同一のアドレススペースのコピーを作成します。1つは親用、もう1つは子用です。両方のプロセスは、fork()呼び出しに続く次のステートメントで実行を開始します。この場合、両方のプロセスは割り当てで実行を開始します
pid = .....;
両方のプロセスは、システムがfork()を呼び出した直後に実行を開始します。両方のプロセスは同一ですが別々のアドレス空間を持っているため、fork()呼び出しの前に初期化された変数は両方のアドレス空間で同じ値を持ちます。すべてのプロセスには独自のアドレス空間があるため、変更は他のプロセスから独立しています。つまり、親が変数の値を変更した場合、変更は親プロセスのアドレス空間内の変数にのみ影響します。 fork()呼び出しによって作成された他のアドレススペースは、同じ変数名を持っている場合でも影響を受けません。
Printfではなくwriteを使用する理由は何ですか?これは、printf()が「バッファリング」されているためです。つまり、printf()はプロセスの出力をグループ化します。親プロセスの出力をバッファリングしている間、子もprintfを使用して一部の情報を出力することがありますが、これもバッファリングされます。結果として、出力はすぐに画面に送信されないため、期待される結果の正しい順序が得られない場合があります。さらに悪いことに、2つのプロセスからの出力が奇妙な方法で混在している可能性があります。この問題を克服するには、「バッファなし」書き込みを使用することを検討できます。
このプログラムを実行すると、画面に次が表示される場合があります。
................
This line is from pid 3456, value 13
This line is from pid 3456, value 14
................
This line is from pid 3456, value 20
This line is from pid 4617, value 100
This line is from pid 4617, value 101
................
This line is from pid 3456, value 21
This line is from pid 3456, value 22
................
プロセスID 3456は、親または子に割り当てられたものです。これらのプロセスは同時に実行されるという事実により、それらの出力行はかなり予測不可能な方法で混在しています。さらに、これらの行の順序はCPUスケジューラーによって決定されます。したがって、このプログラムを再度実行すると、まったく異なる結果が得られる場合があります。
アプリケーションを作成している場合、おそらく日常のプログラミングでforkを使用する必要はありません。
プログラムで何らかのタスクを実行するために別のプログラムを起動したい場合でも、CやPerlの「システム」など、舞台裏でforkを使用する他の簡単なインターフェイスがあります。
たとえば、アプリケーションでbcなどの別のプログラムを起動して計算を実行する場合、「システム」を使用して実行できます。システムは「fork」を実行して新しいプロセスを作成し、次に「exec」を実行してそのプロセスをbcに変換します。 bcが完了すると、システムはプログラムに制御を戻します。
他のプログラムを非同期で実行することもできますが、その方法を覚えていません。
サーバー、シェル、ウイルス、またはオペレーティングシステムを記述している場合は、フォークを使用する可能性が高くなります。
Forkは新しいプロセスを作成します。 forkがなければ、initのみを実行できるUNIXシステムになります。
Fork()は、すべてのボディが記述したとおりに新しいプロセスを作成するために使用されます。
ここにバイナリツリーの形式でプロセスを作成するコードがあります。.....バイナリツリーでプロセスを作成するレベルの数までスキャンするように求められます。
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
int t1,t2,p,i,n,ab;
p=getpid();
printf("enter the number of levels\n");fflush(stdout);
scanf("%d",&n);
printf("root %d\n",p);fflush(stdout);
for(i=1;i<n;i++)
{
t1=fork();
if(t1!=0)
t2=fork();
if(t1!=0 && t2!=0)
break;
printf("child pid %d parent pid %d\n",getpid(),getppid());fflush(stdout);
}
waitpid(t1,&ab,0);
waitpid(t2,&ab,0);
return 0;
}
出力
enter the number of levels
3
root 20665
child pid 20670 parent pid 20665
child pid 20669 parent pid 20665
child pid 20672 parent pid 20670
child pid 20671 parent pid 20670
child pid 20674 parent pid 20669
child pid 20673 parent pid 20669
マルチプロセッシングはコンピューティングの中心です。たとえば、IEまたはFirefoxは、インターネットの閲覧中にファイルをダウンロードするプロセスを作成できます。または、ワードプロセッサでドキュメントを印刷しているときに、まだ別のページを見て、まだそれでいくつかの編集を行います。
fork()
は、子プロセスを生成するために使用されます。通常、スレッド化と同様の状況で使用されますが、違いがあります。スレッドとは異なり、fork()
は完全に独立したプロセスを作成します。つまり、子と親は、fork()
が呼び出された時点で互いに直接コピーされますが、完全に独立しています。どちらも他のメモリ空間にアクセスできません(通常のトラブルを起こさずに他のプログラムのメモリにアクセスします)。
fork()
は、一部のサーバーアプリケーションで引き続き使用されます。ほとんどの場合、ユーザーの要求を処理する前に権限をドロップする* NIXマシンでルートとして実行されるものです。他にもいくつかのユースケースがありますが、ほとんどの人は現在マルチスレッドに移行しています。
最初にfork()システムコールとは何かを理解する必要があります。説明させてください
fork()システムコールは、親プロセスの正確な複製を作成します。親スタック、ヒープ、初期化データ、未初期化データの複製を作成し、読み取り専用モードで親プロセスとコードを共有します。
Forkシステムコールは、コピーオンライトベースでメモリをコピーします。つまり、コピーが必要な場合に、子が仮想メモリページを作成します。
今fork()の目的:
Fork()と新しいプロセスを開始するためのexec()関数を持つことの背後にある理論的根拠は nixスタック交換に関する同様の質問への回答 で説明されています。
基本的に、forkは現在のプロセスをコピーするため、プロセスで使用可能なさまざまなオプションはすべてデフォルトで確立されるため、プログラマーはそれらを提供しません。
対照的に、Windowsオペレーティングシステムでは、プログラマはCreateProcess関数を使用する必要があります。この関数は非常に複雑であり、新しいプロセスのパラメーターを定義するためにさまざまな構造を作成する必要があります。
したがって、要約すると、フォーク(実行)の理由は、新しいプロセスの作成が簡単だからです。
fork()関数は、呼び出し元の既存のプロセスを複製して新しいプロセスを作成するために使用されます。この関数が呼び出される既存のプロセスが親プロセスになり、新しく作成されたプロセスが子プロセスになります。既に述べたように、子は親の複製コピーですが、それにはいくつかの例外があります。
子には、オペレーティングシステムで実行されている他のプロセスと同様に一意のPIDがあります。
子には、プロセスIDのPIDと同じ親プロセスIDがあります
それを作成したプロセス。
子プロセスでリソース使用率とCPU時間カウンターがゼロにリセットされます。
子の保留信号のセットは空です。
子は親からタイマーを継承しません
例:
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
int var_glb; /* A global variable*/
int main(void)
{
pid_t childPID;
int var_lcl = 0;
childPID = fork();
if(childPID >= 0) // fork was successful
{
if(childPID == 0) // child process
{
var_lcl++;
var_glb++;
printf("\n Child Process :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
}
else //Parent process
{
var_lcl = 10;
var_glb = 20;
printf("\n Parent process :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
}
}
else // fork failed
{
printf("\n Fork failed, quitting!!!!!!\n");
return 1;
}
return 0;
}
さて、上記のコードをコンパイルして実行すると:
$ ./fork
Parent process :: var_lcl = [10], var_glb[20]
Child Process :: var_lcl = [1], var_glb[1]
Forkシステムコールの使用は、子プロセスと呼ばれる新しいプロセスを作成します。子プロセスは、プロセス(システムコールフォークと呼ばれるプロセス)と同時に実行され、このプロセスは親プロセスと呼ばれます。
新しい子プロセスが作成されると、両方のプロセスはfork()システムコールに続く次の命令を実行します。子プロセスは、親プロセスで使用する同じpc(プログラムカウンター)、同じCPUレジスタ、同じオープンファイルを使用します。
一部のシステムでは、目的の動作に応じてfork()の異なるバージョンが存在します
一部のUNIXシステムにはfork1()およびforkall()があります
Fork()システムコールは、子プロセスを作成するために使用します。親プロセスの正確な複製です。 Forkは、スタックセクション、ヒープセクション、データセクション、環境変数、コマンドライン引数を親からコピーします。