web-dev-qa-db-ja.com

forkとexecの違い

forkexecの違いは何ですか?

189
Sashi

forkexecの使用は、新しいプロセスを開始する非常に簡単な方法を提供するという点でUNIXの精神を例示しています。

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

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

exec呼び出しは、基本的に現在のプロセス全体を新しいプログラムに置き換える方法です。プログラムを現在のプロセススペースにロードし、エントリポイントから実行します。

したがって、forkexecは、現在のプロセスの子として実行される新しいプログラムを取得するために、順番に使用されることがよくあります。通常、シェルはfindなどのプログラムを実行しようとするたびにこれを行います。シェルはフォークし、その後、子はfindプログラムをメモリにロードし、すべてのコマンドライン引数、標準I/Oなどを設定します。

ただし、一緒に使用する必要はありません。たとえば、プログラムに親コードと子コードの両方が含まれる場合、プログラムがforkingなしでexec自体になることは完全に許容できます(実行に注意する必要があります。各実装には制限があります)。これは、TCPポートとforkのコピーを単にリッスンするデーモンで、親がリッスンに戻っている間に特定のリクエストを処理するために、非常に多く使用されています(今でも使用されています)。

同様に、終了したことを知っており、別のプログラムを実行したいだけのプログラムは、子供に対してforkexec、そしてwaitをする必要はありません。子を直接プロセス空間にロードできます。

一部のUNIX実装には、コピーオンライトと呼ばれるものを使用する最適化されたforkがあります。これは、プログラムがそのスペース内の何かを変更しようとするまで、fork内のプロセススペースのコピーを遅延させるトリックです。これは、プロセス空間全体をコピーする必要がないという点で、forkではなくexecのみを使用するプログラムに役立ちます。

execisforkに続いて呼び出された場合(これがほとんどの場合です)、プロセス空間への書き込みが発生し、それが子プロセスにコピーされます。

exec呼び出しのファミリー全体(execlexecleexecveなど)がありますが、ここでのexecはそれらのいずれかを意味します。

次の図は、bashコマンドを使用してディレクトリをリストするためにlsシェルが使用される典型的なfork/exec操作を示しています。

+--------+
| pid=7  |
| ppid=4 |
| bash   |
+--------+
    |
    | calls fork
    V
+--------+             +--------+
| pid=7  |    forks    | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash   |             | bash   |
+--------+             +--------+
    |                      |
    | waits for pid 22     | calls exec to run ls
    |                      V
    |                  +--------+
    |                  | pid=22 |
    |                  | ppid=7 |
    |                  | ls     |
    V                  +--------+
+--------+                 |
| pid=7  |                 | exits
| ppid=4 | <---------------+
| bash   |
+--------+
    |
    | continues
    V
346
paxdiablo

fork()は、現在のプロセスを2つのプロセスに分割します。または、言い換えると、プログラムについて考えやすいニースリニアは、突然、1つのコードを実行する2つの別個のプログラムになります。

 int pid = fork();

 if (pid == 0)
 {
     printf("I'm the child");
 }
 else
 {
     printf("I'm the parent, my child is %i", pid);
     // here we can kill the child, but that's not very parently of us
 }

これはあなたの心を吹き飛ばすことができます。これで、2つのプロセスによって実行されるほぼ同一の状態の1つのコードができました。子プロセスは、fork()呼び出しが中断したところから開始することを含め、作成したばかりのプロセスのすべてのコードとメモリを継承します。唯一の違いは、あなたが親か子かを知らせるfork()戻りコードです。あなたが親の場合、戻り値は子のIDです。

execは少しわかりやすく、ターゲットの実行可能ファイルを使用してプロセスを実行するようにexecに指示するだけで、同じコードを実行したり、同じ状態を継承したりする2つのプロセスはありません。 @Steve Hawkinsが言うように、execは、forkの後に使用して、現在のプロセスでターゲット実行可能ファイルを実行できます。

52
Doug T.

"Advanced Unix Programming" by Marc Rochkind のいくつかの概念は、fork()/exec()のさまざまな役割を理解するのに、特にWindows CreateProcess() 型:

A program is a collection of instructions and data that is kept in a regular file on disk. (from 1.1.2 Programs, Processes, and Threads)

プログラムを実行するために、カーネルは最初に、プログラムが実行される環境である新しいprocessの作成を要求されます。 (1.1.2プログラム、プロセス、およびスレッドからも)

プロセスとプログラムの違いを完全に理解しないと、execまたはforkシステムコールを理解することはできません。これらの条件が初めての場合は、戻ってセクション1.1.2を確認してください。すぐに続行する準備ができている場合、区別を1つの文に要約します。プロセスは、命令、ユーザーデータ、システムデータセグメント、および実行時に取得される他の多くのリソースで構成される実行環境です。 、プログラムは、プロセスの命令とユーザーデータセグメントを初期化するために使用される命令とデータを含むファイルです。 (5.3 execシステムコールから)

プログラムとプロセスの違いを理解すると、fork()およびexec()関数の動作は次のように要約できます。

  • fork()は、現在のプロセスの複製を作成します
  • exec()は、現在のプロセスのプログラムを別のプログラムに置き換えます

(これは基本的に paxdiabloのより詳細な回答 )の簡易版の「ダミー」バージョンです。

31
Michael Burr

Forkは、呼び出しプロセスのコピーを作成します。一般的に構造に従います enter image description here

int cpid = fork( );

if (cpid = = 0) 
{

  //child code

  exit(0);

}

//parent code

wait(cpid);

// end

(子プロセスtext(code)、data、stackは呼び出しプロセスと同じです)子プロセスはifブロックでコードを実行します。

EXECは、現在のプロセスを新しいプロセスのコード、データ、スタックに置き換えます。一般的に構造に従います enter image description here

int cpid = fork( );

if (cpid = = 0) 
{   
  //child code

  exec(foo);

  exit(0);    
}

//parent code

wait(cpid);

// end

(exec呼び出し後、unixカーネルは子プロセスのテキスト、データ、スタックをクリアし、fooプロセス関連のテキスト/データで埋めます)したがって、子プロセスは異なるコード(fooのコード{親とは異なります})

28
Sandesh Kobal

これらは一緒に使用して、新しい子プロセスを作成します。まず、forkを呼び出すと、現在のプロセス(子プロセス)のコピーが作成されます。次に、execが子プロセス内から呼び出され、親プロセスのコピーを新しいプロセスに「置き換え」ます。

プロセスは次のようになります。

child = fork();  //Fork returns a PID for the parent process, or 0 for the child, or -1 for Fail

if (child < 0) {
    std::cout << "Failed to fork GUI process...Exiting" << std::endl;
    exit (-1);
} else if (child == 0) {       // This is the Child Process
    // Call one of the "exec" functions to create the child process
    execvp (argv[0], const_cast<char**>(argv));
} else {                       // This is the Parent Process
    //Continue executing parent process
}
7
Steve Hawkins

fork()は現在のプロセスのコピーを作成し、fork()呼び出しの直後から新しい子で実行されます。 fork()の後は、fork()関数の戻り値を除いて同じです。 (詳細については、RTFM。)2つのプロセスはさらに分岐する可能性があり、共有ファイルハンドルを介した場合を除き、一方が他方に干渉することはありません。

exec()は、現在のプロセスを新しいプロセスに置き換えます。 fork()とは何の関係もありません。ただし、現在のプロセスを置き換えるのではなく、別の子プロセスを起動する場合、exec()がfork()の後に続くことがよくあります。

4
Warren Young

fork()exec()の主な違いは、

fork()システムコールは、現在実行中のプログラムのクローンを作成します。元のプログラムは、fork()関数呼び出しの次のコード行で実行を継続します。クローンは、コードの次の行からも実行を開始します。 http://timmurphy.org/2014/04/26/using-fork-in-cc-a-minimum-working-example/ から取得した次のコードを見てください

#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
    printf("--beginning of program\n");
    int counter = 0;
    pid_t pid = fork();
    if (pid == 0)
    {
        // child process
        int i = 0;
        for (; i < 5; ++i)
        {
            printf("child process: counter=%d\n", ++counter);
        }
    }
    else if (pid > 0)
    {
        // parent process
        int j = 0;
        for (; j < 5; ++j)
        {
            printf("parent process: counter=%d\n", ++counter);
        }
    }
    else
    {
        // fork failed
        printf("fork() failed!\n");
        return 1;
    }
    printf("--end of program--\n");
    return 0;
}

このプログラムは、fork()ingの前に、ゼロに設定されたカウンター変数を宣言します。 fork呼び出しの後、2つのプロセスが並行して実行され、どちらもカウンターの独自のバージョンをインクリメントします。各プロセスは完了するまで実行され、終了します。プロセスは並行して実行されるため、どちらが先に終了するかを知る方法はありません。このプログラムを実行すると、以下に示すような結果が出力されますが、実行ごとに結果が異なる場合があります。

--beginning of program
parent process: counter=1
parent process: counter=2
parent process: counter=3
child process: counter=1
parent process: counter=4
child process: counter=2
parent process: counter=5
child process: counter=3
--end of program--
child process: counter=4
child process: counter=5
--end of program--

exec()ファミリーのシステムコールは、プロセスの現在実行中のコードを別のコードに置き換えます。プロセスはPIDを保持しますが、新しいプログラムになります。たとえば、次のコードを検討してください。

#include <stdio.h> 
#include <unistd.h> 
main() {
 char program[80],*args[3];
 int i; 
printf("Ready to exec()...\n"); 
strcpy(program,"date"); 
args[0]="date"; 
args[1]="-u"; 
args[2]=NULL; 
i=execvp(program,args); 
printf("i=%d ... did it work?\n",i); 
} 

このプログラムは、execvp()関数を呼び出して、コードを日付プログラムに置き換えます。コードがexec1.cという名前のファイルに保存されている場合、実行すると次の出力が生成されます。

Ready to exec()... 
Tue Jul 15 20:17:53 UTC 2008 

プログラムは、「Ready to exec()」という行を出力します。 。 。そして、execvp()関数を呼び出した後、コードを日付プログラムに置き換えます。行に注意してください―。 。 。その時点でコードが置き換えられたため、機能しましたか?は表示されません。代わりに、「date -u」を実行した結果が表示されます。

2

enter image description herefork()

実行中のプロセスのコピーを作成します。実行中のプロセスは親プロセスと呼ばれ、新しく作成されたプロセスは子プロセスと呼ばれます。 2つを区別する方法は、戻り値を調べることです。

  1. fork()は、親の子プロセスのプロセス識別子(pid)を返します

  2. fork()は、子で0を返します。

exec()

プロセス内で新しいプロセスを開始します。新しいプログラムを現在のプロセスにロードし、既存のプロセスを置き換えます。

fork() + exec()

新しいプログラムを起動するときは、最初にfork()を実行し、新しいプロセスを作成してから、実行するプログラムバイナリをexec()(つまり、メモリにロードして実行)します。

int main( void ) 
{
    int pid = fork();
    if ( pid == 0 ) 
    {
        execvp( "find", argv );
    }

    //Put the parent to sleep for 2 sec,let the child finished executing 
    wait( 2 );

    return 0;
}
1
Yogeesh H T

fork()およびexec()の概念を理解するための主要な例は、Shell、ユーザーがシステムにログインした後に通常実行するプログラム。シェルはcommand lineの最初のWordをcommand名として解釈します

多くのコマンドでは、Shell-forksおよび子プロセスexecsコマンドラインの残りの単語をコマンドのパラメーターとして扱う名前に関連付けられたコマンド.

Shellでは、3種類のコマンドを使用できます。まず、コマンドは実行可能ファイルにすることができます。これには、ソースコードのコンパイルによって生成されたオブジェクトコードが含まれます(たとえばCプログラム)。次に、コマンドは、一連のシェルコマンドラインを含む実行可能ファイルにすることができます。最後に、コマンドは内部シェルコマンドにすることができます(実行可能ファイルの代わりにex-> cdlsなど)

0
krpra