2017-02-21 14 views
-1

私は初心者であり、fork()とwait()関数がどのように動作するのかを学んでいます。フォーク/待機プログラム。私の出力は何でしょうか?私の出力は正しいですか?

誰かが自分のコードを実行し、自分の出力が何であるべきか教えてください。

今私が取得しています: B C B C に D Eを

しかし、私の仲間は、それがあるべきと言う: B C A D E A B C

もう一つは、それがあるべきと言う:ので、待ち時間()関数の B C C D E

、私は子プロセスが親の前に終了すると思っていました。そのため、私は出力が「E」で終わると考えています。

可能な出力は何でしょうか?私はそれを実行するとき、私はABCABCADEを得ることを理解していない。最初の子プロセスのために一度だけ印刷されるべきではありませんか?

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

int main(void) { 
int pid; 

    pid= fork(); 
    if (pid == 0) { 
     fprintf(stdout, "A\n"); 
     pid= fork(); 
     if (pid==0) { 
      fprintf(stdout, "B\n"); 
      pid=fork(); 
      fprintf(stdout, "C\n"); 
     } 
     else { 
      wait(NULL); 
      fprintf(stdout, "D\n"); 
     } 
    } 
    else { 
     fprintf(stdout, "E\n"); 
     wait(NULL); 
    } 
    // your code goes here 
    return(0); 
} 
+0

「あなたのコードはここに」どこに書いていますか?とにかく出力は確定的ではありません。異なるランで異なる結果を得ることができます。また、出力をパイプしたり、出力をファイルにリダイレクトしたりすると、別の結果を再度得ることができます。 –

答えて

0

ときにfork()、またはとにかくどのくらいのプロセスが実行されていること、または他が引き継ぐ前にそれを取得どこまで、または実際には両者が異なるコア上で同時に実行するかどうか、親や子どもが最初に実行するかどうかを指定されていません。親が子供のために首尾よくwait()である場合、そのwait()が返されるとすぐに、子供が終了したことが確かです。しかし、他の同期手段がない場合、子供が実行した動作に対して、wait()を介してその子供を収集し、収集する間に親の行動の順序を予測することは不可能である。

fork()呼び出しの戻り値に関する条件も確認してください。成功したfork()は、子(唯一)に0を返します。そのため、プログラムの動作の大部分はそれぞれ正確に1つのプロセスに結び付けられます。

しかし、もう1つの要因もあります。同じオープンファイル記述の複数のハンドルの相互作用です。 fork()の場合、2つのstdoutストリーム(同じオープンファイルの説明を参照)が2つあります。 POSIXは、プログラムがその状況をどのように処理しなければならないかについて、some restrictionsを配置します。プログラムの標準出力が行バッファリングされている場合、これは端末に接続されているときのデフォルトです。プログラムの動作は、出力される各文字列の最後の改行のためによく定義されます。しかし、stdoutが完全にバッファリングされている場合、パイプに接続されているように、振る舞いを定義する前にfflush(stdout)にする必要があります。したがって、フォーク前にfflush()には最も安全です。プログラムの動作が実行環境に関係なく定義されるようにしてください。

これらの点を考慮してプログラムを分析し、プログラムが定義された動作をまったく実行すると仮定すると、複数の可能な出力がありますが、それらの間には提案はありません。あなたのプログラムが動作を未定義にするような方法で実行されている場合、出力については何も言えません。

+0

可能な出力は何でしょうか?私はそれを実行するとき、私はABCABCADEを得ることを理解していない。最初の子プロセスのために一度だけ印刷されるべきではありませんか? – SuperHippo

+0

@SuperHippo、これは良い点です。私の答えを更新する必要があります。一番下の行は、あなたのプログラムがun * specified *の動作だけでなく、完全なun * defined *の動作を示すことです。 –

+0

@SuperHippo、更新されました。 –

1

あなたははEを印刷した後までwait()なかったのでEは、最後に表示される必要があることを理由はありません。

ラインバッファリングされた出力を必ずしも使用していないという複雑な問題があります。フォークする前に保留中の出力がある場合、親と子の両方がバッファリングされたテキストを出力します。

fork()の前にfflush(stdout);を追加しましょう。これを行うと、複数のA出力が取り除かれ、残りの理由については理由が分かります。

parent 
| 
| 
+------\ 
|  | 
"E" "A" 
|  | 
wait +------\ 
.  |  | 
.  wait "B" 
.  .  | 
.  .  +------\ 
.  .  |  | 
.  .  "C" "C" 
.  .  | 
.  |<----exit 
.  "D" 
.  | 
|<----exit 
| 

あなたはEが任意の時点で印刷することができることを見ることができますが、Dは後少なくとも1 Cまでは印刷されません(左側1):ここではタイムラインです。

あなたは

fprintf(stdout, "E\n"); 
    wait(NULL); 

の順番を入れ替えた場合は(順番に少なくとも1 C後である)Eは常にD後に来ることを確認することができますが、何がありますように、他のCはまだ、最後のものはありませんそのプロセスの終了との順序関係。

0

出力が完全に確定していないため、異なる実行で異なる結果が得られる可能性があります。また、出力をパイプしたり、ファイルにリダイレクトしたりする場合は、そうでない場合とは異なる結果になります。詳細はprintf() anomaly after fork()を参照してください。ここで

は強制的にフラッシュすることができ、出力が(それはあまりにも、代わりに非標準<wait.h>ヘッダのPOSIX標準<sys/wait.h>ヘッダーを使用しています。私のためにほとんどの場合

#include <stdio.h> 
#include <unistd.h> 
#include <sys/wait.h> 

static int flush = 0; 

static void print(const char *str) 
{ 
    printf("%s\n", str); 
    if (flush) 
     fflush(stdout); 
} 

int main(int argc, char **argv) 
{ 
    if (argc > 1) 
     flush = (argv[argc] == 0); 
    int pid = fork(); 
    if (pid == 0) 
    { 
     print("A"); 
     pid = fork(); 
     if (pid == 0) 
     { 
      print("B"); 
      pid = fork(); 
      print("C"); 
     } 
     else 
     { 
      wait(NULL); 
      print("D"); 
     } 
    } 
    else 
    { 
     print("E"); 
     wait(NULL); 
    } 
    return(0); 
} 

、ときにあなたのコードの改正です(なしのリダイレクト、コマンドライン引数なし)無料で実行され、Eが最初に表示されます。

E 
A 
B 
C 
C 
D 

catにパイプすると、出力が変化するが、テーマにバリエーションを次のようになります。

A 
B 
C 
A 
D 
A 
B 
C 
E 

A 
B 
C 
A 
B 
C 
A 
D 
E 

発生するフラッシュを強制するために、引数を指定して実行すると、私は一貫してこれを取得:

E 
A 
B 
C 
D 
C 

スケジューラは異なるでのプロセスの実行を許可されています配列;私のマシン上のスケジューラーは異なる順序でプロセスを実行します。親プロセスは通常、wait()に達するまで実行されますが、子プロセスは直ちにスケジュールされないため、その出力は子プロセスの前に表示されます。

YMMV。

Mac(MacOS Sierra 10.12.3、GCC 6.3.0)でテスト済みです。

関連する問題