2015-09-28 94 views
2

我是新來的管道,並試圖創建一對管道,允許子進程寫入父進程,並與父進程進行通信。有一位父母最多可以有4個孩子。這個孩子與執行官成爲一個不同的計劃。瞭解管道,重定向和IPC

我有什麼工作:

從父到子進程寫。當我讀入子程序的stdin時,它會收到我從父母寫的內容。

目的:

要創建一個紙牌遊戲,以每個客戶的父會談(子進程),並給出了所有的動作和信息給他們,從它的標準輸出到孩子們的標準輸入。個別的孩子進程退回他們的標準輸出,由主要父母閱讀。遊戲的動作完全由一個序列決定,而不是玩家。所以這是一個機器人遊戲。

我堅持什麼:

我不知道如何得到它,家長可以通過文件流中讀取孩子的標準輸出。當我嘗試從子行設置讀取時,代碼似乎停止工作。甚至連孩子都不能從父母那裏讀到(似乎停止在現在註釋掉的留給父母的留置權)。

我也不確定如何「等待」,直到出現某種情況。就像,在開始時,玩家必須發回「準備好」的消息給父母,讓他們知道他們正在工作。一旦我發送孩子的「準備好」,我如何無限期地「等待」,直到下一條消息出現?

我不知道我是否正確設置了管道。有人可以提供關於如何使用通信管道並確認我的邏輯的指導嗎?

我收集什麼讓家長寫來的孩子是:

  1. 創建管道第一
  2. 叉掉父進程到另一個進程(子)
  3. 管道在連接到父stdout和使用DUP2,靠近
  4. 連接管的出孩子的標準輸入封閉讀出側父,並用DUP2封閉寫作部分的兒童和密切
  5. 使用文件描述符中的fdopen()獲取文件流,然後打印到該文件流。
  6. 子進程stdin現在是無論您打印到父級的標準輸出。

這是正確的嗎?我嘗試將這種邏輯應用於孩子到父母,但將其顛倒過來。

  1. 將管道連接到讀取文件流,該文件流是子程序從標準輸出寫入的地方。
  2. 將輸出管道連接到父流讀取的讀取流。

    void start_child_process(Game *game, int position) { 
        int child2Parent[2]; 
        int parent2Child[2]; 
    
        if (pipe(parent2Child)) { 
         printf("PIPE FAIL!\n"); 
        } 
        if (pipe(child2Parent)) { 
         printf("PIPE FAIL!\n"); 
        } 
    
        pid_t pid = fork(); 
        game->readStream[position] = fdopen(child2Parent[0], "r"); 
        game->writeStream[position] = fdopen(parent2Child[1], "w"); 
    
        if (pid) { // Parent 
         // Write from parent to child 
         close(parent2Child[0]); 
         dup2(fileno(game->writeStream[position]), STDOUT_FILENO); 
         fprintf(game->writeStream[position], "%s", "test message"); 
         fflush(game->writeStream[position]); 
         close(parent2Child[1]); 
    
         // Read from child -- not working 
    
         /*dup2(child2Parent[0], STDIN_FILENO); 
         close(child2Parent[0]); 
         close(child2Parent[1]); 
         */ 
    
        } else { 
         // Setup child to read from stdin from parent 
         dup2(parent2Child[0], STDIN_FILENO); 
         close(parent2Child[1]); 
    
         // Setup writing from child to parent 
    
         /* 
         if (dup2(child2Parent[1], STDOUT_FILENO) == -1) { 
          fprintf(stderr, "dup2 in child failed\n"); 
         } else { 
          fprintf(stderr, "dup2 in child successful\n"); 
          close(child2Parent[0]); 
          close(child2Parent[1]); 
         } 
         */ 
    
    
         if ((int)execl("child", "2", "A", NULL) == -1) { 
          printf("Failed child process\n"); 
         } 
    
        } 
    } 
    

我的孩子主要具有讀取它的情況如下:

char string[100]; 
printf("reading from pipe: %s\n", fgets(string, 100, stdin)); 

但我不知道如何

另外,我也不允許使用POPEN()或write()。我也鼓勵顯然使用文件流。

回答

1

我主要談到在父母和子女過程之間建立雙向溝通的主要問題。如果您想要額外的答案,請提出不同的問題。

您似乎有一個合理的通用方法,但您確實有一個嚴重的誤解/設計缺陷:儘管多個客戶端將每個標準流連接到管道以與父進程通信是合理的,但您無法連接如果您希望一次能夠處理多個客戶端,則父節點將所有這些管道的末端連接到父節點的標準流。畢竟,父母只有一套標準流。爲了支持多個客戶端,父進程必須爲每個客戶端維護一對獨立的文件描述符和/或流,並且必須通過它們而不是通過其標準流進行通信。

我不確定爲什麼你的父母/孩子的溝通失敗,當你勾住孩子對父母的方向。該過程確實類似於設置另一個其他端點。這是一個工作示例:


parent.c:

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

int main() { 
    int child2Parent[2]; 
    int parent2Child[2]; 
    char buffer[256]; 
    FILE *p2cStream; 
    FILE *c2pStream; 
    pid_t pid; 

    if (pipe(parent2Child) || pipe(child2Parent)) { 
     perror("Failed to create pipes"); 
     exit(EXIT_FAILURE); 
    } 

    switch (pid = fork()) { 
    case -1: /* error */ 
     perror("Failed to fork"); 
     break; 
    case 0: /* child */ 
     // Setup child to read from stdin from parent 
     close(parent2Child[1]); /* ignoring any error */ 
     close(child2Parent[0]); /* ignoring any error */ 
     if ((dup2(parent2Child[0], STDIN_FILENO) < 0) 
      || (dup2(child2Parent[1], STDOUT_FILENO) < 0)) { 
     perror("Failed to duplicate file descriptors"); 
     } else { 
     /* conventionally, the first program argument is the program name */ 
     /* also, execl() returns only if it fails */ 
     execl("child", "child", "2", "A", NULL); 
     perror("Failed to exec child process"); 
     } 

     exit(EXIT_FAILURE); 
     break; 
    default: /* parent */ 
     close(parent2Child[0]); /* ignoring any error */ 
     close(child2Parent[1]); /* ignoring any error */ 
     if (!(p2cStream = fdopen(parent2Child[1], "w")) 
      || !(c2pStream = fdopen(child2Parent[0], "r"))) { 
     perror("Failed to open streams"); 
     exit(EXIT_FAILURE); 
     } 
     if ((fprintf(p2cStream, "test message from parent\n") < 0) 
      || fclose(p2cStream)) { 
     perror("Failed to write to the child"); 
     exit(EXIT_FAILURE); 
     } 
     if (fscanf(c2pStream, "%255[^\n]", buffer) < 1) { 
     perror("Failed to read the child's message"); 
     exit(EXIT_FAILURE); 
     } 
     printf("The child responds: '%s'\n", buffer); /* ignoring any error */ 
     break; 
    } 

    return EXIT_SUCCESS; 
} 

child.c:

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <errno.h> 

int main(int argc, char *argv[]) { 
    char buffer[256] = { 0 }; 

    if (scanf("%255[^\n]", buffer) < 0) { 
     perror("Failed to reading input"); 
     exit(EXIT_FAILURE); 
    } 

    /* 
    * If stdout is connected to the parent then we must avoid 
    * writing anything unexpected to it 
    */ 
    if (fprintf(stderr, "received: '%s'\n", buffer) < 0) { 
     perror("Failed to echo input"); 
     exit(EXIT_FAILURE); 
    } 

    printf("Hi, Mom!\n"); /* ignoring any error */ 
    fflush(stdout);  /* ignoring any error */ 

    return EXIT_SUCCESS; 
} 

相反你的代碼,的確注意到

  • 注意檢查可能表示我關心的錯誤的所有返回值;
  • 父母使用標準流以外的流與孩子進行交流(儘管只有一個孩子,這是一種方便而非必需);
  • 該約定爲execl()參數。

還要注意,至於等待某些東西「出現」,IPC流上的I/O操作會自動產生這種效果。然而,你如何能夠或應該如何使用這個問題當然是一個不同的問題。

+0

嗨,感謝您的幫助。所以我對你的代碼做了一些修改。孩子:http://pastebin.com/6iq2n1JU,家長:http://pastebin.com/UPbbG6qg。但我得到的輸出是:http://pastebin.com/g50gAeW1 這是基本閱讀父母和孩子之間的正確方法嗎?就像父母發送「Hi」一樣,等孩子說出「Hi Mom」,然後說「你好嗎?」回到孩子身邊,等待回覆? – user3139573

+0

如果您對我提供的代碼的工作原理有何疑問,或者爲什麼它會按照原樣編寫,請隨時詢問。 –

+0

我在寫完之前不小心發表了評論,對不起。每次寫完後你是否應該關閉文件流(p2cStream)?我可以把它變成一個函數,比如write_to_child(char * message)那兩行嗎? – user3139573