fork()の目的は何ですか?


87

Linuxの多くのプログラムやマニュアルページで、を使用しfork()たコードを見てきました。なぜ使用する必要fork()があり、その目的は何ですか?


150
すべての食事する哲学者が飢えないように。
kenj0418 2009年

回答:


106

fork()Unixで新しいプロセスを作成する方法です。を呼び出すとfork、独自のアドレス空間を持つ独自のプロセスのコピーが作成されます。これにより、複数のタスクを、それぞれがマシンのフルメモリを持っているかのように、互いに独立して実行できます。

使用例を次に示しforkます。

  1. あなたのシェルの使用forkコマンドラインから起動するプログラムを実行します。
  2. ApacheのようなWebサーバーはfork、複数のサーバープロセスを作成するために使用し、各プロセスは独自のアドレス空間で要求を処理します。1つが死んだりメモリリークが発生したりしても、他の人は影響を受けないため、フォールトトレランスのメカニズムとして機能します。
  3. Google Chromefork、個別のプロセス内で各ページを処理するために使用します。これにより、1ページのクライアント側コードがブラウザ全体をダウンさせるのを防ぐことができます。
  4. fork一部の並列プログラム(MPIを使用して記述されたプログラムなど)でプロセスを生成するために使用されます。これは、独自のアドレス空間を持たず、プロセス内に存在するスレッドを使用する場合とは異なることに注意してください。
  5. スクリプト言語は、fork子プロセスを開始するために間接的に使用します。たとえばsubprocess.Popen、Pythonのようにコマンドを使用するたびに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それが完了するのを待ってから、独自の実行を続行します。この方法でフォークを使用する必要はないことに注意してください。並列プログラムのように、いつでも多くの子プロセスを生成でき、それぞれがプログラムを同時に実行する場合があります。基本的に、Unixシステムで新しいプロセスを作成するときはいつでも、を使用していfork()ます。同等のWindowsについては、を参照してくださいCreateProcess

より多くの例とより長い説明が必要な場合は、ウィキペディアにまともな要約があります。また、最新のオペレーティングシステムでプロセス、スレッド、および同時実行性がどのように機能するかについてのスライドをいくつか示します。


箇条書き5:「しばしば」?「しばしば」だけ?どちらがそれを使用しないか、またはどのような状況でfork()が使用されないか-fork()をサポートするシステムで、つまり。
Jonathan Leffler

19
不思議なことに、それはCreateProcess()と呼ばれています-それらのクレイジーなWindowsの人:-)
paxdiablo 2009年

2
「シェルはフォークを使用して、コマンドラインから呼び出すプログラムを実行する」までは実現しませんでした。
Lazer 2010年

1
スライドのリンクが壊れています
piertoni 2015年

1
すべての答えは、それfork()がUNIXで新しいプロセスを作成する方法であると言っていますが、衒学的であるために、少なくとも1つは他にありますposix_spawn()
Davislor 2018

15

fork()は、Unixが新しいプロセスを作成する方法です。fork()を呼び出した時点で、プロセスが複製され、2つの異なるプロセスがそこから実行を続行します。それらの1つである子はfork()で0を返します。もう1つである親は、fork()で子のPID(プロセスID)を返します。

たとえば、シェルで次のように入力すると、シェルプログラムはfork()を呼び出してから、渡したコマンド(この場合はtelnetd)を子で実行し、親もプロンプトを再度表示します。バックグラウンドプロセスのPIDを示すメッセージとして。

$ telnetd &

新しいプロセスを作成する理由としては、それがオペレーティングシステムが同時に多くのことを実行できる方法です。そのため、プログラムを実行し、実行中に別のウィンドウに切り替えて別のことを行うことができます。


@varDumperグッドキャッチ!
ダニエルC.ソブラル2018

9

fork()は、子プロセスを作成するために使用されます。fork()関数が呼び出されると、新しいプロセスが生成され、fork()関数呼び出しは子と親に対して異なる値を返します。

戻り値が0の場合は、自分が子プロセスであり、戻り値が数値(たまたま子プロセスID)である場合は、自分が親であることがわかります。(負の数の場合、フォークは失敗し、子プロセスは作成されませんでした)

http://www.yolinux.com/ TutorialS / ForkExecProcesses.html


1
戻り値が-1でない限り、その場合、fork()は失敗しました。
Jonathan Leffler

8

fork()は基本的に、この関数を呼び出すプロセスの子プロセスを作成するために使用されます。fork()を呼び出すと、子IDにゼロが返されます。

pid=fork()
if pid==0
//this is the child process
else if pid!=0
//this is the parent process

これにより、親と子に異なるアクションを提供し、マルチスレッド機能を利用できます。


6

fork()は、親と同じ新しい子プロセスを作成します。したがって、その後のコードで実行するものはすべて、両方のプロセスによって実行されます。たとえば、サーバーがあり、複数のリクエストを処理する場合に非常に便利です。


なぜ親と同じ子を作成するのですか?

1
それは、軍隊と1人の兵士を作るようなものです。プログラムが1つずつではなく、同時により多くのリクエストを処理できるようにフォークします。
cloudhead 2009年

fork()は、子に0を返し、親に子のpidを返します。その後、子はexec()のような呼び出しを使用して、その状態を新しいプログラムに置き換えることができます。これがプログラムの起動方法です。
トッドガンブリン

プロセスはほぼ同じですが、微妙な違いがたくさんあります。明らかな違いは、現在のPIDと親PIDです。保持されているロックと保持されているセマフォに関連する問題があります。POSIXのfork()マニュアルページには、親と子の25の違いがリストされています。
Jonathan Leffler

2
@kar:2つのプロセスがあると、そこから個別に続行でき、そのうちの1つがそれ自体(exex())を別のプログラムに完全に置き換えることができます。
Vatine 2010

4

アプリケーションを作成している場合は、日常のプログラミングでフォークを使用する必要はおそらくありません。

プログラムで別のプログラムを起動してタスクを実行したい場合でも、Cやperlの「システム」など、バックグラウンドでフォークを使用する他のより単純なインターフェイスがあります。

たとえば、アプリケーションでbcなどの別のプログラムを起動して計算を実行したい場合は、「system」を使用して実行できます。システムは「フォーク」を実行して新しいプロセスを作成し、次に「実行」を実行してそのプロセスをbcに変換します。bcが完了すると、システムは制御をプログラムに戻します。

他のプログラムを非同期で実行することもできますが、その方法を思い出せません。

サーバー、シェル、ウイルス、またはオペレーティングシステムを作成している場合は、フォークを使用する可能性が高くなります。


ありがとうございますsystem()fork()CコードでPythonスクリプトを実行したいので、読んでいました。
ビーンタクシー

4

Forkは新しいプロセスを作成します。フォークがなければ、initしか実行できないUNIXシステムになります。


4

システムコール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
     ................

プロセスID3456は、親または子に割り当てられたものである可能性があります。これらのプロセスは同時に実行されるため、それらの出力ラインはかなり予測できない方法で混在しています。さらに、これらの行の順序はCPUスケジューラによって決定されます。したがって、このプログラムを再度実行すると、まったく異なる結果が得られる可能性があります。


3
テキストをコピーして貼り付ける代わりに、次のリンクにコメントすることもできます:csl.mtu.edu/cs4411.ck/www/NOTES/process/fork/create.html
chaitanya lakkundi 2016

3

マルチプロセッシングはコンピューティングの中心です。たとえば、IEまたはFirefoxは、インターネットを閲覧しているときにファイルをダウンロードするプロセスを作成できます。または、ワードプロセッサでドキュメントを印刷しているときに、別のページを表示したり、編集したりすることもできます。


3

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

2

まず、fork()システムコールとは何かを理解する必要があります。説明させてください

  1. fork()システムコールは、親プロセスの正確な複製を作成します。親スタック、ヒープ、初期化されたデータ、初期化されていないデータの複製を作成し、読み取り専用モードでコードを親プロセスと共有します。

  2. forkシステムコールは、コピーオンライトベースでメモリをコピーします。これは、コピーが必要な場合に、子が仮想メモリページで作成することを意味します。

fork()の目的:

  1. Fork()は、サーバーが複数のクライアントを処理する必要があるなど、作業の分割がある場所で使用できます。したがって、親は定期的に接続を受け入れる必要があります。したがって、サーバーは各クライアントに対してフォークを実行して読み取り/書き込みを実行します。

1

fork()子プロセスを生成するために使用されます。通常、スレッド化と同様の状況で使用されますが、違いがあります。スレッドとは異なり、fork()完全に別個のプロセスを作成します。つまり、子と親は、その時点で相互に直接コピーされます。fork()は、呼び出されますが、完全に別個であり、どちらも他方のメモリ空間にアクセスできません(通常の問題は発生しません)。別のプログラムのメモリにアクセスします)。

fork()一部のサーバーアプリケーションでは引き続き使用されます。ほとんどの場合、ユーザー要求を処理する前にアクセス許可を削除する* NIXマシンでrootとして実行されます。他にもいくつかのユースケースがありますが、ほとんどの人は現在マルチスレッドに移行しています。


2
「ほとんどの人」がマルチスレッドに移行したという認識がわかりません。プロセスはここにとどまり、スレッドもそうです。どちらからも「先に進んだ」人はいません。並列プログラミングでは、最大かつ最も並行するコードは、分散メモリマルチプロセスプログラム(MapReduceやMPIなど)です。それでも、ほとんどの人はマルチコアマシン用にOpenMPまたはいくつかの共有メモリパラダイムを選択し、GPUは最近スレッドを使用していますが、それ以外にもたくさんあります。ただし、このサイトでは、マルチスレッド化されたものよりも多くのコーダーがサーバー側でプロセスの並列処理に遭遇しているに違いありません。
トッドガンブリン

1

fork()の背後にある理論的根拠と、新しいプロセスを開始するためのexec()関数を持っているだけの理由は、unixスタック交換に関する同様の質問への回答で説明されています

基本的に、forkは現在のプロセスをコピーするため、プロセスのさまざまな可能なオプションはすべてデフォルトで確立されているため、プログラマーはそれらを提供していません。

対照的に、Windowsオペレーティングシステムでは、プログラマーはCreateProcess関数を使用する必要があります。これは、はるかに複雑で、新しいプロセスのパラメーターを定義するためにさまざまな構造を設定する必要があります。

したがって、要約すると、(実行ではなく)フォークする理由は、新しいプロセスを簡単に作成できるためです。


0

Fork()システムコールを使用して子プロセスを作成します。これは、親プロセスの正確な複製です。フォークは、スタックセクション、ヒープセクション、データセクション、環境変数、コマンドライン引数を親からコピーします。

参照:http//man7.org/linux/man-pages/man2/fork.2.html


0

フォーク()関数は、それが呼び出され、そこから既存のプロセスを複製して新しいプロセスを作成するために使用されます。この関数が呼び出される既存のプロセスが親プロセスになり、新しく作成されたプロセスが子プロセスになります。すでに述べたように、子は親の複製コピーですが、いくつかの例外があります。

  • 子は、オペレーティングシステムで実行されている他のプロセスと同様に一意のPIDを持っています。

  • 子には、
    それを作成したプロセスの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]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.