2016-03-22 4 views
1

まず、いくつかの魔法を見る準備をしてください。
こんにちは、私はこの問題で過去の時間に不満を抱き、苦労しています。なぜ子プロセスが死ぬのか理解できません。私は基本的に1つの親プロセスと多くの子プロセスを持っています。すべての子供は親とコミュニケーションする必要があり、親はすべての子供とコミュニケーションを取る必要があります。まず始めに、子どもたちは常にreadにしようとしましたが、私の親は何も送信せず、ちょうどwriteのパイプの最後を閉じて、readsのブロックを止めました。ここに私のプロセスです(私のマクロdefは5の子プロセスです)。 パイプの閉鎖にもかかわらず読み取りがブロックされ続けます

  • 2パイプを指す5 int*の配列を作成して始めます。親は子供に話をする最初のものを使用し、子供が二1を使用しています を
  • フォーク5子どもたちとそれぞれの子が継続的に
  • 親がすべての書き込みを閉じ読み取ろうとするパイプ
  • のappropiate両端を閉じますパイプの終わり、子供のreadループが
  • 親が
  • 親は私がここ
  • を死ぬ子どもの死亡を待って終了する必要がありますので、私のコード(S)

    #include <unistd.h> 
    #include <stdio.h> 
    #include <stdlib.h> 
    #include <string.h> 
    #include <strings.h> 
    #include <sys/types.h> 
    #include <sys/select.h> 
    #include <sys/types.h> 
    #define PROCESSES 5 
    
    
    int main(int argc, char ** argv) { 
    
        int * pipes[PROCESSES]; 
        for (int i = 0; i < PROCESSES; i++) { 
         pipes[i] = malloc(sizeof(int) * 2); 
         if (pipe(pipes[i]) == -1) { 
          perror("Error piping"); 
          exit(1); 
         } 
        } 
    
        //PIDS we will wait on 
        int children_pids[PROCESSES]; 
    
        for (int i = 0; i < PROCESSES; i++) { 
         int status = fork(); 
         switch(status) { 
          case -1: 
           perror("Error forking a child"); 
           exit(1); 
          case 0: 
           //Close the pipes we don't need 
           close(pipes[i][1]); 
           //Inside the child process, die immediately 
           char buffer[128] = ""; 
           while (read(pipes[i][0], buffer, 127) > 0) { 
            //Keep reading and doing nothing 
           } 
           printf("Dying\n"); 
           exit(1); 
          default: 
           //Parent process, close the pipes we don't need 
           close(pipes[i][0]); 
           break; 
         } 
         //Parent continue spawning children 
         children_pids[i] = status; 
        } 
    
        //CLOSE ALL PIPES FROM PARENT TO CHILDREN------------ 
        for (int i = 0; i < PROCESSES; i++) { 
         if (close(pipes[i][1]) == -1) { 
          perror("Error closing a pipe"); 
          exit(1); 
         } 
        } 
    
        //AWAIT CHILDREN DEATHS 
        for (int i = 0; i < PROCESSES; i++) { 
         wait(&children_pids[i]); 
        } 
        printf("All children have died"); 
        return 0; 
    } 
    

    私はそれが取り除かれたとき、それが正常に動作しますので、死んでから子供をブロックしている子供のreadループであることを知って。しかし、なぜこれが当てはまるのか分かりません。下の私のループでは、私は明らかにすべてのパイプを閉じ、エラーをチェックします。どうしてこれなの?! readはまだ私の道で私のreturn;の目標を達成することができませんでしたか?

    +0

    各chileは 'dup'ed fd ...を持っています。したがって、「書き込み終了」を閉じると、すべての子プロセスが終了するまで子プロセスfdで終了しません...書き込み終了子プロセスで終了し、参照カウントを1に戻します。 – Myst

    +0

    @Myst、OPは以下を閉じます。 'close(pipes [i] [1]);' – SergeyA

    +3

    @SergeyA - それらのうちの1つだけが閉じられます。オペレーションはそれぞれの「i」のために閉じるべきである – Myst

    答えて

    4

    まず、私はあなたが明らかに知っているいくつかの情報を確認しましょう:以下

    は、修正されたコード[ご容赦ください無償スタイルのクリーンアップ]です。私はこれを書いています。なぜなら、他の人もこの答えを読むかもしれないし、答えにいくつかの文脈を持つことが常に良いからです。

    であなたのコード、なぜその後、私が表示されます:あなたがよく知っているように、それぞれの子プロセスがのdupエドのコピーを受け取り

    case 0: // the child process 
        // close all input endpoints (input only performed by root process) 
        // also close all irrelevant output endpoints: 
        for (int j = 0; j < PROCESSES; j++){ 
         close(pipes[j][1]); 
         if(j != i) 
         close(pipes[j][0]); 
        } 
    

    case 0: // the child process 
        close(pipes[i][1]); // <- if (i == 2) pipes[1][1] is open. 
    

    は、おそらく次のタスクを実行していることを意図ファイル記述子(fd)と各pipeは、入力(読み込み)と出力(書き込み)の2つのファイル記述子で構成されています。

    このプロセスをフォークするたびに、,オープンパイプの両方のこれらのエンドポイント(ファイル記述子)が複製されます。

    readは、受信データが最終的に到着する可能性がある間にブロックされます。つまり、少なくとも1つの「出力」(書き込み)ファイルディスクリプタがまだ開いている間は、readはブロックされます。

    次の例では、パイプを1つ開き、プロセスをフォークします。フォークされたプロセスは、エンドポイントの「入力」(書き込み)を閉じ、readを呼び出します。 readは、親プロセスにまだ1つのオープン入力fdがあるためブロックします(覚えておいて、fdが複製されました)。親が閉じた後、 "入力" fdがなくなり、書き込みエンドポイントがなくなり、読み込みが失敗します(ブロックを停止します)。

    パイプに何も書き込んでいないことに注意してください。

    #include <stdio.h> 
    #include <stdlib.h> 
    #include <unistd.h> 
    
    typedef struct { 
        int in; // the input fd 
        int out; // the output fd 
    } pipe_io; 
    
    int main() { 
        // the container; 
        pipe_io io; 
        // make the pipe 
        pipe((int*)&io); 
        // forking will duplicate the open files 
        pid_t child; 
        if (!(child = fork())) { // fork==0 => we're in the child process 
        close(io.out);   // closing one reading access point. 
        char buff[4]; 
        // read will block because there's still an open writing access point. 
        printf("Child waiting (read will block)\n"); 
        read(io.in, buff, 1); 
        // cleanup and exit process. 
        close(io.in); 
        printf("Child exits (read stopped blocking once all inputs were closed)\n"); 
        exit(0); 
        } 
        sleep(1); // wait... 
        printf("closing parent's writing (output) endpoint.\n"); 
        close(io.out); 
        sleep(1); // wait... 
        printf("closing parent's reading (input) endpoint.\ndone.\n"); 
        waitpid(child, NULL, 0); 
    } 
    

    出力は、コードの制御フローのための明確な兆候です:だから

    Child waiting (read will block) 
    closing parent's writing (output) endpoint. 
    Child exits (read stopped blocking once all inputs were closed) 
    closing parent's reading (input) endpoint. 
    done. 
    

    、(いないブロックを)失敗するreadへの呼び出しのためのために、我々はすべての書き込みエンドポイントを閉じる必要があります/チャネル。

    コードにはすべてのプロセス用のパイプがありましたが、各プロセスが他のプロセスの「入力」(書き込み)エンドポイントを開いたままにすることができました。readは常にブロックされます。おそらく、書くためのもの

    case 0: // the child process 
        // This line only closes this process's input stream, but this stream is 
        // open for all other processes: 
        close(pipes[i][1]); // <- if (i == 2) pipes[1][1] is open. 
    
        //... 
    
        // `read` will ALWAYS block because other processes keep input endpoints. 
        while (read(pipes[i][0], buffer, 127) > 0) { 
         //Keep reading and doing nothing 
        } 
        printf("Dying\n"); 
        exit(1); 
    

    case 0: // the child process 
        // closing all input endpoints (input only performed by root process) 
        // also closing all irrelevant output endpoints: 
        for (int j = 0; j < PROCESSES; j++){ 
         close(pipes[j][1]); 
         if(j != i) 
         close(pipes[j][0]); 
        } 
    
        //... 
    

    P.S.

    各プロセスに別々の役割がない限り、すべてのプロセスに対してパイプを開くのはあまり一般的ではありません。

    同じ機能を共有するすべてのプロセスが同じパイプを共有するのが普通です。

    たとえば、プロセスのファミリを使用して共有されたタスクファミリを実行する場合、どのプロセスがどのタスクを実行するかは重要でない可能性があるため、タスクを共有パイプに提出すると効果的です。データを読み取る最初のプロセスがタスクを実行するプロセスです。

    プロセスがビジー状態のときは、パイプからの読み取りではなく、別のプロセスが利用可能である(「読み取り」でブロックされている)場合、すぐに処理されます(ビジープロセスを待つのではなく) 。

    この単一パイプ設計は、「待機」期間を最小限に抑え、スケジューリング上の懸案事項(パイプのバッファの制限を除く)を削除します。

    +0

    "親が "input" fdを閉じた後、書き込みエンドポイントはなくなり、read willすべてのファイル記述子が重複していますが、これは恐らく 'STDIN_FILENO'がデフォルトの' 1'ですが、子プロセスでは閉じません。なぜ 'read'をブロックしないのでしょうか? –

    +0

    @demonware - 私が参照している入力エンドポイントは、 'read'が読み込んでいる同じパイプに書き込むものです。 'stdin'は' read'で使われているパイプに書き込んでいないので、それは無関係です。生物学の種のようにそれについて考える。特定の種の女性がそれ以上存在しない場合、その種は絶滅する(繁殖のために女性が必要と仮定して)...他の種のメスはあまり役に立たない;-) – Myst

    +1

    申し訳ありませんが、私は 'STDOUT'を意味しました。なぜ、 'pipe [1] [1]'は 'pipe [2] [0]'の読み込みに影響を与えるのでしょうか? –

    4

    いくつかあります。

    (1)各子について、[親子]のパイプは1つしか作成していませんが、2番目の[子から親子]のパイプが必要です(パイプはではなく、のような双方向のソケットです)。

    (2)あなたは事前に作成をすべてパイプは、子供に、あなたはその2本のパイプのためない現在の子供のためだけでなく、パイプの両側のあるものを閉じる必要があります。

    あなたはこれを行わない場合、子Nがため子の2本のパイプ N. ないすべての子供たちのために [と]側面を開いたままになります

    ファイルディスクリプタはトンで開いていたものは何でもの [与え]フォーク、親が完全に開いたパイプを閉じた場合、子意志まだ継承[コピー]した後、彼はフォークの時に親です。あなたの元のプログラムが何をしたかである子供

    すべてのために - がまだ開いてそれらを保持しているので、だから、親に閉じても効果はありません。

    私のバージョンでは、それほど重大ではありません。 Preclose(childclose経由)がなければ、子0はそれ自身のパイプだけを開いています。しかし、子供1は開いた子供0のパイプを保持します。子供2は子供0と子供1のパイプを開いたままにします。そして、等しく...

    このように、多くの子供たちは互いにパイプ記述子を開いています。したがって、親プロセスがパイプを閉じても、他の子には開いているので、子供はEOFを表示します

    これを可視化するには、元のコードをfork後の子供の最初の実行可能部分(例えばcase 0直後)の操作を行います。

    { 
        pid_t pid = getpid(); 
        char buf[100]; 
        printf("DEBUG: %d\n",pid); 
        sprintf(buf,"ls -l /proc/%d/fd",pid); 
        system(buf); 
    } 
    

    は、標準入力/標準出力/標準エラー出力、代わりに予想2(する必要があります4)オープン記述子の無視して、あなたは(2 * PROCESSES)が表示されますの各(つまり10個の)記述子をそれぞれという子に置き換えます。あなたは[親に】このようなシーケンスを繰り返し可能性があり、あなたがまだ [それぞれの子が閉じていますマイナス2]同じものが表示されます親の最後のクローズを行った後


    これは、構造体によって簡単に構成できます。実際に動作していることを証明するために、エコーバックを使用して実際のデータ転送を追加しました。私はまた、その違いを示すためにデバッグのオプションをいくつか追加しました。

    #include <unistd.h> 
    #include <stdio.h> 
    #include <stdlib.h> 
    #include <string.h> 
    #include <strings.h> 
    #include <sys/types.h> 
    #include <sys/select.h> 
    #include <sys/wait.h> 
    
    #define PROCESSES 5 
    
    int opt_n;        // do _not_ close other children 
    int opt_p;        // original semantics 
    int opt_v;        // show list 
    
    // child control 
    struct child { 
        int cld_idx;      // child index 
        pid_t cld_pid;      // child's pid 
        int cld_status;      // child's exit status 
        int cld_topar[2];     // pipe: child-to-parent 
        int cld_tocld[2];     // pipe: parent-to-child 
    }; 
    
    #define CLOSEME(_fd) \ 
        do { \ 
         if (_fd >= 0) \ 
          close(_fd); \ 
         _fd = -1; \ 
        } while (0) 
    
    struct child children[PROCESSES]; 
    
    // fdlist -- output list of open descriptors 
    void 
    fdlist(struct child *cld,const char *reason) 
    { 
        struct child cld2; 
        char cmd[100]; 
    
        if (cld == NULL) { 
         cld = &cld2; 
         cld->cld_pid = getpid(); 
         cld->cld_idx = -1; 
        } 
    
        printf("\n"); 
        printf("fdlist: idx=%d pid=%d (from %s)\n", 
         cld->cld_idx,cld->cld_pid,reason); 
    
        sprintf(cmd,"ls -l /proc/%d/fd",cld->cld_pid); 
        system(cmd); 
    } 
    
    // childclose -- close any pipe units from other children 
    void 
    childclose(int i) 
    { 
        struct child *cld; 
    
        for (int j = 0; j < PROCESSES; ++j) { 
         if (j == i) 
          continue; 
         cld = &children[j]; 
         CLOSEME(cld->cld_topar[0]); 
         CLOSEME(cld->cld_topar[1]); 
         CLOSEME(cld->cld_tocld[0]); 
         CLOSEME(cld->cld_tocld[1]); 
        } 
    } 
    
    // childopen -- create pipes for child 
    void 
    childopen(int i) 
    { 
        struct child *cld; 
    
        cld = &children[i]; 
    
        // to cut down on the clutter, only create the pipes as we need them 
        pipe(cld->cld_topar); 
        pipe(cld->cld_tocld); 
    } 
    
    // childstart -- start up child 
    void 
    childstart(int i) 
    { 
        struct child *cld; 
        pid_t pid; 
    
        cld = &children[i]; 
    
        // to cut down on the clutter, only create the pipes as we need them 
        if (! opt_p) 
         childopen(i); 
    
        pid = fork(); 
        if (pid < 0) { 
         perror("Error forking a child"); 
         exit(1); 
        } 
    
        switch (pid) { 
        case 0: // child 
         // close any pipe that doesn't belong to us 
         if (! opt_n) 
          childclose(i); 
    
         pid = getpid(); 
         cld->cld_pid = pid; 
    
         if (opt_v) 
          fdlist(cld,"childstart"); 
    
         // Close the pipe sides we don't need 
         CLOSEME(cld->cld_topar[0]); 
         CLOSEME(cld->cld_tocld[1]); 
    
         // Inside the child process, die immediately 
         int len; 
         char buffer[128]; 
    
         while (1) { 
          len = read(cld->cld_tocld[0], buffer, sizeof(buffer) - 1); 
          if (len <= 0) 
           break; 
    
          // Keep reading and echoing 
          write(cld->cld_topar[1],buffer,len); 
         } 
    
         printf("child %d: Dying\n",i); 
         exit(1); 
         break; 
    
        default: // parent 
         // give child time to print message 
         if (opt_v) 
          sleep(1); 
    
         cld->cld_pid = pid; 
    
         // Parent process, close the pipe sides we don't need 
         CLOSEME(cld->cld_topar[1]); 
         CLOSEME(cld->cld_tocld[0]); 
    
         break; 
        } 
    } 
    
    int 
    main(int argc, char **argv) 
    { 
        char *cp; 
        struct child *cld; 
        int len; 
        char buf[128]; 
    
        --argc; 
        ++argv; 
    
        for (; argc > 0; --argc, ++argv) { 
         cp = *argv; 
         if (*cp != '-') 
          break; 
    
         switch (cp[1]) { 
         case 'n': // do _not_ close other descriptors 
          opt_n = 1; 
          break; 
         case 'p': // preopen all pipes 
          opt_p = 1; 
          break; 
         case 'v': // show verbose messages 
          opt_v = 1; 
          break; 
         } 
        } 
    
        setlinebuf(stdout); 
    
        printf("main: pipes will be created %s\n", 
         opt_p ? "all at once" : "as needed"); 
        printf("main: other child descriptors %s be closed\n", 
         opt_n ? "will not" : "will"); 
    
        for (int i = 0; i < PROCESSES; i++) { 
         cld = &children[i]; 
         cld->cld_idx = i; 
         cld->cld_topar[0] = -1; 
         cld->cld_topar[1] = -1; 
         cld->cld_tocld[0] = -1; 
         cld->cld_tocld[1] = -1; 
        } 
    
        // create pipes for _all_ children ahead of time 
        if (opt_p) { 
         for (int i = 0; i < PROCESSES; i++) 
          childopen(i); 
         if (opt_v) 
          fdlist(NULL,"master/OPEN"); 
        } 
    
        // start up all children 
        for (int i = 0; i < PROCESSES; i++) 
         childstart(i); 
    
        // show final list 
        if (opt_v) { 
         sleep(1); 
         for (int i = 0; i < PROCESSES; i++) { 
          cld = &children[i]; 
          fdlist(cld,"master/POSTSTART"); 
         } 
        } 
    
        // send to child 
        for (int i = 0; i < PROCESSES; i++) { 
         cld = &children[i]; 
         len = sprintf(buf,"child %d, you are pid %d\n",i,cld->cld_pid); 
         write(cld->cld_tocld[1],buf,len); 
        } 
    
        // receive from child 
        printf("\n"); 
        for (int i = 0; i < PROCESSES; i++) { 
         cld = &children[i]; 
         len = read(cld->cld_topar[0],buf,sizeof(buf)); 
         printf("RECV(%d): %s",i,buf); 
        } 
    
        // show final list 
        if (opt_v) { 
         sleep(1); 
         for (int i = 0; i < PROCESSES; i++) { 
          cld = &children[i]; 
          fdlist(cld,"master/FINAL"); 
         } 
        } 
    
        // CLOSE ALL PIPES FROM PARENT TO CHILDREN------------ 
        for (int i = 0; i < PROCESSES; i++) { 
         cld = &children[i]; 
         CLOSEME(cld->cld_topar[0]); 
         CLOSEME(cld->cld_tocld[1]); 
        } 
    
        // AWAIT CHILDREN DEATHS 
        for (int i = 0; i < PROCESSES; i++) { 
         cld = &children[i]; 
         waitpid(cld->cld_pid,&cld->cld_status,0); 
        } 
    
        printf("All children have died\n"); 
    
        return 0; 
    } 
    
    +0

    ちょうど興味がありますが、なぜ個々の子どもの 'read'がブロックされるのでしょうか?'read'は特定のパイプ(すべてではない)にあり、実際にはすべて(親からの)すべてを閉じます。 –

    関連する問題