2017-10-22 14 views
1

私は現在オペレーティングシステムと並行性を研究していますが、プロセススケジューラに関する私のプラクティスの1つは、Linuxで複数のプロセスがどのように「並列」で動作するかをC言語で把握し、ここに私のコードは次のとおりです。LinuxのCでfork()によって生成された子プロセスのシェルでの出力リダイレクトはどのように機能しますか?

/* This file's name is Task05_3.c */ 
#include <sys/types.h> 
#include <sys/wait.h> 
#include <sys/time.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <signal.h> 
#include <errno.h> 
#include <string.h> 

int kill(pid_t pid, int sig); 
unsigned usleep(unsigned seconds); 

#define NUMBER_OF_PROCESSES 7 
#define MAX_EXPERIMENT_DURATION 4 

long int getDifferenceInMilliSeconds(struct timeval start, struct timeval end) 
{ 
    int seconds = end.tv_sec - start.tv_sec; 
    int useconds = end.tv_usec - start.tv_usec; 
    int mtime = (seconds * 1000 + useconds/1000); 
    return mtime; 
} 

int main(int argc, char const *argv[]) 
{ 
    struct timeval startTime, currentTime; 
    int diff; 

    int log[MAX_EXPERIMENT_DURATION + 2] = {-1}; 
    /* initialization */ 
    for (int k = 0; k < MAX_EXPERIMENT_DURATION + 2; ++k) 
     log[k] = -1; 

    gettimeofday(&startTime, NULL); 

    pid_t pid_for_diss = 0; 

    for (int i = 0; i < NUMBER_OF_PROCESSES; ++i) 
    { 
     pid_for_diss = fork(); 
     if (pid_for_diss < 0) { 
      printf("fork error, errno(%d): %s\n", errno, strerror(errno)); 
     } else if (pid_for_diss == 0) { 
      /* This loop is for logging when the child process is running */ 
      while (1) { 
       gettimeofday(&currentTime, NULL); 
       diff = getDifferenceInMilliSeconds(startTime, currentTime); 
       if (diff > MAX_EXPERIMENT_DURATION) 
       { 
        break; 
       } 
       log[diff] = i; 
      } 
      // for (int k = 0; k < MAX_EXPERIMENT_DURATION + 2; ++k) 
      // { 
      //  if (log[k] != -1) 
      //  { 
      //   printf("%d, %d\n", log[k], k); 
      //  } 
      // } 
      // exit(0); 
      break; 
     } 
    } 

    /* This loop is for print the logged results out */ 
    if (pid_for_diss == 0) 
    { 
     for (int k = 0; k < MAX_EXPERIMENT_DURATION + 2; ++k) 
     { 
      if (log[k] != -1) 
      { 
       printf("%d, %d\n", log[k], k); 
      } 
     } 
     kill(getpid(), SIGKILL); 
    } 

    int status; 
    while (wait(&status) != -1);// -1 means wait() failed 
    printf("Bye from the parent!\n"); 
} 

は基本的に、ここでの私の考えでは(私はフォークで7つのプロセスを生成するために、親プロセスのループのために設定されていることである)とを競うためにそれらを強制whileループにそれらを設定しますある期間内のCPUの使用。そして、子プロセスが実行されるようにスケジュールされるたびに、現在の時間と親プロセスの開始時間との差を、実行中の子プロセスに属するアレイに記録します。それから、7つのプロセスがすべてwhileループを壊した後、私はそれぞれの子プロセスが別のループを設定して、ログに記録された結果を表示します。 最初に、forループの外側に印刷するためのループを設定しました(私のコードでわかるように) 、と私はbashで直接./Task05_3を実行し、ここでの結果である:

[email protected]:osc$ gcc -std=c99 Task05_3.c -o Task05_3 
[email protected]:osc$ ./Task05_3 
5, 0 
4, 0 
6, 0 
4, 1 
1, 0 
4, 2 
4, 3 
4, 4 
0, 0 
1, 1 
6, 1 
1, 2 
1, 3 
1, 4 
5, 1 
5, 2 
5, 3 
5, 4 
6, 2 
6, 3 
2, 0 
6, 4 
2, 1 
2, 2 
2, 3 
2, 4 
0, 1 
3, 0 
0, 2 
0, 3 
0, 4 
3, 1 
3, 2 
3, 3 
3, 4 
Bye from the parent! 
[email protected]:osc$ 

あなたは(親プロセスと子プロセスの両方から)すべての結果は、端末との結果にプリントアウトされていることをここで見ることができます子プロセスはランダムな順序になっています(これは、同時に複数のプロセスが標準出力に書き込むことが原因と考えられます)。しかし、./Task05_3 > 5output_c.csvで実行しようとすると、私のターゲットとなる.csvファイルには、親プロセスからの結果しか含まれていないことがわかります。Result_in_csv01

私の最初の質問は、どのように.csvファイル親プロセスのプロンプトが含​​まれていますか?それは、私がbashでタイプした命令は、親プロセスの出力をリダイレクトするだけで、子プロセスの出力ストリームとは何の関係もないからですか?

メジャーなforループの中にforループ(印刷用)を入れようとすると(上記のコードのforループを参照してください)、./Task05_3 > 5output_c.csvでコードを実行すると、ファイルは次のようになります:Result_in_csv02

これですべての結果が表示されます!子プロセスの結果の順序は、もはやランダムではありません! (実行中の子プロセスがすべての結果を出力するまで、他の子プロセスは明らかに待機しています)。だから私の2番目の質問は、私が単に私のforループの位置を変更した後にこれがどうして起こるかということです。

PS。私はあるに私のコードを実行したLinuxマシン:

[email protected]:osc$ cat /proc/version 
Linux version 3.10.0-693.2.2.el7.x86_64 ([email protected]) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC)) #1 SMP Tue Sep 12 22:26:13 UTC 2017 

そして、GCCのバージョンは次のとおりです。

[email protected]:osc$ gcc --version 
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-16) 
Copyright (C) 2015 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions. There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 

答えて

4

出力stdio関数を経由して、デフォルトではバッファリングされます。それは即座に書かれたものではなく、何らかの内部構造(FILEの内部)に蓄積されていることを意味します。 3つの可能性があります。

  • FILEはバッファリングされていません。出力はすぐに書き込まれます。
  • バッファリングされた行。出力は、バッファがいっぱいになるか、'\n'(改行)が見られるときに書き込まれます。
  • ブロックされたブロック。出力はバッファがいっぱいになると書き込まれます。

fflushを使用して手動で強制的に書き込みを強制することはできます。

ファイルあなたは(fopenで)開くには、デフォルトではバッファリングブロックされています。 stderrはバッファリングされていません。 stdoutは、端末を参照する場合はラインバッファリングされ、それ以外の場合はブロックされます。

あなたの子プロセスは、フルライン(printf("%d, %d\n", log[k], k);)を印刷します。これは、stdoutが端末に行く限り、すべてが直ちに表示されることを意味します(行がバッファされているためです)。

しかし、あなたは、出力をファイルにリダイレクト、stdoutは、バッファブロックになります。バッファーはかなり大きいので、すべての出力がバッファーに蓄積されます(バッファーがいっぱいになることはありません)。 FILEハンドルが閉じられたとき(通常、fclose)、バッファがフラッシュされる(すなわち書き込まれ、空になる)。通常、プログラムが終了すると、すべてのファイルが自動的に閉じられる(mainからexitに電話する)。

ただし、この場合には、あなたが(致命的な、キャッチできない)信号を送信してプロセスを終了します。つまり、ファイルが決して閉じられず、バッファが書き込まれず、内容が失われます。だからこそあなたは何の出力も見ません。


2番目のバージョンでは、自分に信号を送信する代わりにexitに電話をかけます。これにより、ハンドラを呼び出すと、開いているファイルをすべて閉じ、バッファをフラッシュして、通常のクリーンアップが実行されます。ところで


、代わりの kill(getpid(), X)あなたは raise(X)を書くことができます。それはより短くて可搬性があります( raiseは標準Cです)。

+0

お返事と説明してくれてありがとう、それは本当にはっきりと便利です。ちなみに、私はまだ私の子プロセスの出力が、ファイルバッファに格納された状態で端末にランダムに印刷されているため、ファイルにリダイレクトしたとき(ブロックバッファの場合、あなたが言ったように)もう少し具体的に説明できますか? Thx again〜 – TyBcodar

+0

@TyBcodar 2番目のバージョンでは、(exit(0) '呼び出しの一環として)各子に1つのアトミック' write() 'しか実行されないので、1つのプロセスのすべての出力一度に一緒に出現する。最初のバージョンでは、すべてのプロセスのすべての行が、すぐに独立して書き込まれます。 – melpomene

関連する問題