web-dev-qa-db-ja.com

このプログラムが「fork!」を印刷するのはなぜですか。 4回?

このプログラムが「fork!」を4回印刷するのはなぜですか?

#include <stdio.h>
#include <unistd.h>

int main(void) {

  fork() && (fork() || fork());

  printf("forked!\n");
  return 0;
}
75
Rawan Lezzeik

最初のfork()は、呼び出しプロセス(p0と呼ぶ)でゼロ以外の値を返し、子(p1と呼ぶ)で0を返します。

P1では、_&&_のショートサーキットが使用され、プロセスはprintfを呼び出して終了します。 p0では、プロセスは式の残りを評価する必要があります。次に、fork()を再度呼び出して、新しい子プロセス(p2)を作成します。

P0ではfork()はゼロ以外の値を返し、_||_の短絡が取られるため、プロセスはprintfを呼び出して終了します。

P2では、fork()は0を返すため、||の残りは最後のfork();を評価する必要がありますこれにより、p2の子が作成されます(p3と呼びます)。

次に、P2はprintfを実行して終了します。

P3はprintfを実行して終了します。

4 printfsが実行されます。

84

1つはmain()からのもので、他の3つはすべてのfork()からのものです。

3つのforks()がすべて実行されることに注意してください。 ref をご覧ください。

戻り値

正常に完了すると、fork()は0を子プロセスに返し、子プロセスのプロセスIDを親プロセスに返します。両方のプロセスはfork()関数から引き続き実行されます。そうでない場合、-1が親プロセスに返され、子プロセスは作成されず、エラーを示すためにerrnoが設定されます。

here のように、プロセスIDをゼロにすることはできません。


それでは、実際に何が起こるのでしょうか?

我々は持っています:

_fork() && (fork() || fork());
_

したがって、最初のfork()は、ゼロ以外のプロセスIDを親に返しますが、子プロセスには0を返します。つまり、論理式の最初のフォークは親プロセスではtrueと評価され、子プロセスではfalseと評価され、 Short circuit evaluation により、残りを呼び出しません2つのfork() s。

そのため、少なくとも2つの印刷物(1つはメインから、もう1つは1番目のfork())を取得することがわかりました。

これで、親プロセスの2番目のfork()が実行され、実行され、ゼロ以外の値が親プロセスに返され、ゼロが子プロセスに返されます。

したがって、親プロセスは最後のfork()まで実行を継続しません(短絡のため)。一方、_||_の最初のオペランドは0なので、子プロセスは最後のフォークを実行します。

つまり、さらに2枚の印刷物が得られるということです。

その結果、合計4枚の印刷物が得られます。


ショートサーキット

ここで、短絡は、基本的に&&の最初のオペランドがゼロの場合、他のオペランドは評価されないことを意味します。同じ論理で、||のオペランドが1の場合、残りのオペランドは評価する必要がありません。これは、残りのオペランドは論理式の結果を変更できないため、実行する必要がないため、時間を節約できるためです。

以下の例を参照してください。


プロセス

親プロセスが子プロセスを作成し、その子プロセスが他のプロセスなどを作成することを忘れないでください。これは、プロセスの階層(または、言うことができるツリー)につながります。

これを念頭に置いて、この 同様の問題this の答えを見てみる価値があります。


説明的な画像

助けになるこの図も作ったと思います。 pidのfork()は、呼び出しごとに3、4、5であると仮定しました。

fork nodes いくつかのfork() sの上に赤いXが付いていることに注意してください。これは、論理式の短絡評価のために実行されないことを意味します。

演算子_&&_の第1オペランドが0であるため、最上部のfork() sは実行されません。したがって、式全体が0になり、残りの実行の本質はありません。 _&&_のオペランド。

一番下のfork()は実行されません。これは_||_の第2オペランドであり、その第1オペランドはゼロ以外の数値であるため、式の結果はすでに評価されています2番目のオペランドが何であってもtrue。

また、次の図では、プロセスの階層を確認できます。 Process hierarchy 前の図に基づいています。


短絡の例

_#include <stdio.h>

int main(void) {

  if(printf("A printf() results in logic true\n"))
    ;//empty body

  if(0 && printf("Short circuiting will not let me execute\n"))
    ;
  else if(0 || printf("I have to be executed\n"))
    ;
  else if(1 || printf("No need for me to get executed\n"))
    ;
  else
  printf("The answer wasn't nonsense after all!\n");

  return 0;
}
_

出力:

_A printf() results in logic true
I have to be executed
_
201
gsamaras

すべてのダウンボッターにとって、これはマージされたが異なる質問によるものです。せいありがとうございました。

問題を3行に分解できます。最初の行と最後の行はどちらもプロセスの数を単に2倍にします。

fork() && fork() || fork();

演算子は短絡しているため、次のようになります。

       fork()
      /      \
    0/        \>0
 || fork()     && fork()
     /\            /   \
    /  \         0/     \>0
   *    *     || fork()  *
                /   \
               *     *

したがって、これはそれぞれ1行を印刷する4 * 5 = 20プロセスです。

注:何らかの理由でfork()が失敗した場合(たとえば、プロセスの数に制限がある場合)、-1が返され、異なる結果が得られます。

14
Karoly Horvath

fork() && (fork() || fork())を実行すると、どうなりますか

forkは、それぞれpid(親)と0(子)の値を持つ2つのプロセスを提供します

最初のフォーク:

  • 親戻り値はpid not null => && (fork() || fork()) を実行します
    • 2番目のfork親値はpidであり、nullではなく_||_ part => print forkedの実行を停止します
    • 2番目のfork子値= 0 => || fork() を実行します
      • 3番目のフォークの親プリントforked
      • 3番目のフォークの子プリントforked
  • 子の戻り値は0です&& partの実行を停止=> prints forked

合計:4 forked

9
Serge Ballesta

私はすでに提出されたすべての答えが好きです。おそらく、printfステートメントにさらにいくつかの変数を追加すると、何が起きているかを簡単に確認できるようになります。

#include<stdio.h>
#include<unistd.h>

int main(){

   long child = fork() && (fork() || fork());
   printf("forked! PID=%ld Child=%ld\n", getpid(), child);
   return 0;
}

私のマシンでは、次の出力が生成されました。

forked! PID=3694 Child = 0
forked! PID=3696 Child = 0
forked! PID=3693 Child = 1
forked! PID=3695 Child = 1
8
Bradley Slavik

このコード:

fork();
fork() && fork() || fork();
fork();

20個のプロセスを取得し、Printfが20回実行します。

そして

fork() && fork() || fork();

printfは合計5回実行されます。

5
Mayank Dixit