2013-12-12 206 views
1

我正在嘗試編寫一個代碼,它可以分叉一個子進程並使用管道與它進行通信。我使用兩個管道 - 一個用於寫入,另一個用於讀取子流程的標準流。這是我到目前爲止有:使用兩個管道的雙向進程間通信

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <signal.h> 
#include <string.h> 

void read_move(int fd) 
{ 
    FILE *stream = fdopen(fd, "r"); 
    char c; 
    setvbuf(stream, NULL, _IONBF, BUFSIZ); 
    while ((c = fgetc(stream)) != EOF) 
    { 
     putchar(c); 
    } 
    fclose(stream); 
} 

void write_move(int fd, const char *move) 
{ 
    FILE *stream = fdopen(fd, "w"); 
    setvbuf(stream, NULL, _IONBF, BUFSIZ); 
    fprintf(stream, "%s", move); 
    fclose(stream); 
} 

int main() { 
    pid_t pid; 
    int wpipe[2]; 
    int rpipe[2]; 
    if (pipe(wpipe) || pipe(rpipe)) 
    { 
     fprintf(stderr, "Pipe creation failed.\n"); 
     return EXIT_FAILURE; 
    } 
    pid = fork(); 
    if (pid == 0) 
    { 
     /* gnuchess process */ 
     setvbuf(stdin, NULL, _IONBF, BUFSIZ); 
     setvbuf(stdout, NULL, _IONBF, BUFSIZ); 
     setvbuf(stderr, NULL, _IONBF, BUFSIZ); 
     dup2(wpipe[0], STDIN_FILENO); 
     dup2(rpipe[1], STDOUT_FILENO); 
     dup2(rpipe[1], STDERR_FILENO); 
     close(wpipe[0]); 
     close(wpipe[1]); 
     close(rpipe[0]); 
     close(rpipe[1]); 
     if (execl("/usr/games/gnuchess", "gnuchess", "-x", NULL) == -1) 
     { 
      fprintf(stderr, "Exec failed.\n"); 
      return EXIT_FAILURE; 
     } 
     return EXIT_SUCCESS; 
    } 

    /* parent process */ 

    printf("Sending move to GNU Chess... \n"); 
    close(wpipe[0]); /* Close other end */ 
    write_move(wpipe[1], "c3\n"); 

    printf("Reading response... \n"); 
    close(rpipe[1]); /* Close other end */ 
    read_move(rpipe[0]); 

    /* clean up */ 
    close(wpipe[1]); 
    close(rpipe[0]);  

    /* kill gnuchess */ 
    kill(pid, SIGTERM); 

    return EXIT_SUCCESS; 
} 

上述程序的輸出是

Sending move to GNU Chess... 
Reading response... 

它卡在read_move函數的調用getline,等待輸入。但我不明白這是可能的,因爲我已經關閉了另一端。

我在做什麼錯?

編輯: 我改變了read_move方法,並在dup2調用後關閉子進程中的管道端點。

回答

2

您在子進程中沒有關閉wpipe。所以當你啓動它時,你實際上傳遞了7個文件描述符給gnu國際象棋 - 重複的stdin,stdout和stderr; wpipe中的2個描述符和rpipe中的2個描述符。

如果你在linux上,在程序運行時找出gnu chess的進程ID,並執行ls/proc // fd來查看它的所有文件描述符。

一旦添加

close(wpipe [0]); close(wpipe [1]); close(rpipe [0]); close(rpipe [1]);在dup2()和execl()之間,你應該沒問題。 (這樣做仍然省略了錯誤處理,就像其中一個dup2()失敗一樣,或者更糟糕的是,在調用pipe()之前,你的程序已經關閉了fds [0,1,2]之一,所以其中一個標準描述符被管道意外取代,但我想這不是重點。)

+0

非常感謝您指出了這一點。它確實有7個文件描述符。但是,在我關閉了4個管道末端之後,ls/proc/PID/fd仍給我4個FD - 0 1 2 3. FD 3爲青色,其他爲紅色。這是什麼意思? – prvnsmpth

+0

另外,關閉管道末端並不能完全解決問題 - 程序進入無限循環。子進程不斷地從管道的末端讀取同樣的東西 - 雖然我只寫了一次。 – prvnsmpth

+0

這可能是因爲gnuchess不能很好地處理EOF。嘗試貓| gnuchess,然後按^ D - gnuchess只是忽略EOF而不是退出。如果你做了一個ls -l到/ proc/PID/fd,你會得到更多關於哪個fd連接到什麼的信息。 –

相關問題