web-dev-qa-db-ja.com

Forループでfork()に視覚的に何が起こるか

fork() 動作を理解しようとしています。今回はfor-loopで。次のコードを確認してください。

#include <stdio.h>

void main()
{
   int i;

   for (i=0;i<3;i++)
   {
      fork();

      // This printf statement is for debugging purposes
      // getppid(): gets the parent process-id
      // getpid(): get child process-id

      printf("[%d] [%d] i=%d\n", getppid(), getpid(), i);
   }

   printf("[%d] [%d] hi\n", getppid(), getpid());
}

ここに出力があります:

[6909][6936] i=0
[6909][6936] i=1
[6936][6938] i=1
[6909][6936] i=2
[6909][6936] hi
[6936][6938] i=2
[6936][6938] hi
[6938][6940] i=2
[6938][6940] hi
[1][6937] i=0
[1][6939] i=2
[1][6939] hi
[1][6937] i=1
[6937][6941] i=1
[1][6937] i=2
[1][6937] hi
[6937][6941] i=2
[6937][6941] hi
[6937][6942] i=2
[6937][6942] hi
[1][6943] i=2
[1][6943] hi

私は非常に視覚的な人物なので、真に物事を理解するための唯一の方法は図を描くことです。私のインストラクターは、8つのhiステートメントがあると言った。コードを書いて実行したところ、実際には8つのhiステートメントがありました。しかし、私は本当にそれを理解していませんでした。そこで、次の図を描きました。

enter image description here

コメントを反映するために図が更新されました:)

観察:

  1. 親プロセス(メイン)はループを3回繰り返す必要があります。次に、printfが呼び出されます
  2. 親forループの各反復で、fork()が呼び出されます
  3. 各fork()呼び出しの後、iが増分されるため、すべての子は、増分される前にiからforループを開始します
  4. 各forループの終わりに、「hi」が印刷されます

私の質問は次のとおりです。

  • 図は正しいですか?
  • なぜtwo出力にi=0のインスタンスがありますか?
  • Fork()の後、各子にiのどの値が引き継がれますか? iの同じ値が引き継がれる場合、「分岐」はいつ停止しますか?
  • 2^n - 1がフォークされた子の数を数える方法であるということは常にありますか?それで、ここでn=3、つまり2^3 - 1 = 8 - 1 = 7の子、これは正しいですか?
62
lucidgold

forループから始めて、これを理解する方法を説明します。

  1. ループは親で開始します、_i == 0_

  2. fork() s、子1を作成します。

  3. これで2つのプロセスができました。両方とも_i=0_を出力します。

  4. ループは両方のプロセスで再起動し、現在は_i == 1_です。

  5. 親と子1 fork()、子2と3を作成します。

  6. これで4つのプロセスができました。 4つすべてが_i=1_を出力します。

  7. 4つのプロセスすべてでループが再開され、現在は_i == 2_です。

  8. 親と子1〜3はすべてfork()で、子4〜7を作成します。

  9. これで8つのプロセスができました。 8つすべてが_i=2_を出力します。

  10. 8つのプロセスすべてでループが再開され、現在は_i == 3_です。

  11. _i < 3_がtrueではなくなったため、8つのプロセスすべてでループが終了します。

  12. 8つのプロセスすべてがhiを出力します。

  13. 8つのプロセスすべてが終了します。

したがって、_0_は2回、_1_は4回、_2_は8回、hiは8回印刷されます。

38
Paul Griffiths
  1. はい、それは正しいです。 (下記参照)
  2. いいえ、_i++_が実行されますafterforkの呼び出し。これがforループの動作方法だからです。
  3. すべてが成功した場合、はい。ただし、forkは失敗する可能性があることに注意してください。

2つ目の説明:

_for (i = 0;i < 3; i++)
{
   fork();
}
_

と類似しています:

_i = 0;
while (i < 3)
{
    fork();
    i++;
}
_

したがって、分岐したプロセス(親と子の両方)のiは、インクリメント前の値です。ただし、増分はfork()の直後に実行されるため、私の意見では、ダイアグラムは正しいものとして扱われる可能性があります。

12
Yu Hao

質問に1つずつ答えるには:

ダイアグラムは正しいですか?

はい、基本的に。それも非常に素晴らしい図です。

つまり、_i=0_などのラベルを完全なループ反復を参照していると解釈するのは正しいことです。ただし、図does n'tが示すのは、各fork()の後、fork()呼び出しの後の現在のループ反復の一部も実行されるということです。分岐した子プロセスによって。

出力に_i=0_の2つのインスタンスがあるのはなぜですか?

printf()の後にfork()があるため、親プロセスと分岐したばかりの子プロセスの両方によって実行されます。 printf()fork()の前に移動すると、親によってのみ実行されます(子プロセスがまだ存在しないため)。

iのどの値がfork()の後に各子に引き継がれますか? iの同じ値が引き継がれる場合、「分岐」はいつ停止しますか?

iの値はfork()によって変更されないため、子プロセスは親と同じ値を参照します。

fork()について覚えておくべきことは、1回呼び出されますが、親プロセスで1回、新しくクローンされた子プロセスで1回、2回返されることです。

より簡単な例として、次のコードを検討してください。

_printf("This will be printed once.\n");
fork();
printf("This will be printed twice.\n");
fork();
printf("This will be printed four times.\n");
fork();
printf("This will be printed eight times.\n");
_

fork()によって作成された子プロセスは、その親の(ほぼ)正確なクローンであるため、独自の観点からは、親プロセスの状態(すべての変数を含む)値、呼び出しスタック、実行中の命令)。即時の唯一の違い(getpid()によって返されるプロセスIDなどのシステムメタデータを除く)は、fork()の戻り値です。これは、子プロセスではゼロですが、ゼロ以外(実際には、親の子プロセスのID)。

_2^n - 1_がフォークされた子の数を数える方法であるというのは常に事実ですか?だから、ここで_n=3_、これは_2^3 - 1 = 8 - 1 = 7_の子を意味し、正しいですか?

fork()を実行するすべてのプロセスは2つのプロセスに変わります(fork()が失敗する可能性のある異常なエラー状態を除きます)。親と子が同じコードを実行し続ける場合(つまり、fork()の戻り値、または独自のプロセスIDをチェックせず、それに基づいて異なるコードパスに分岐する)、その後の各フォークプロセス数が2倍になります。そのため、はい、3つの分岐の後、合計で2³= 8プロセスになります。

3
Ilmari Karonen