2011-02-12 70 views
2

我正在嘗試編寫一些使用管道在父進程和它的子進程之間進行通信的代碼。但是,我的管道在我第一次使用後似乎放棄了(也就是說,在第一次使用管道後停止工作)。我不確定如何解決這個問題,任何幫助將不勝感激。我也知道,我在這裏使用的一些編碼練習並不是很理想(主要是使用睡眠)。在C問題中的管道通信

const int READ = 0; 
const int WRITE = 1; 
char* COOP = "Criminal cooperates\n"; 
char* SIL = "Criminal doesn't talk\n"; 

char* reader(int); 
void writer(int, char *c); 

int main() 
{  
    int c1pipe1[2]; 
    int c1pipe2[2]; 
    int c2pipe1[2]; 
    int c2pipe2[2]; 
    int c1sentence = 0; 
    int c2sentence = 0; 
    int r; 
    int c; 
    pipe(c1pipe1); 
    pipe(c1pipe2); 
    pipe(c2pipe1); 
    pipe(c2pipe2); 
    int C2; 
    int C1 = fork(); 
    if(C1 > 0) 
     C2 = fork(); 
    if(C1 < 0 || C2 < 0) //error 
    { 
     perror("fork() failed"); 
     exit(1); 
    } 

    else if(C1 == 0) 
    { 
     close(c1pipe1[WRITE]); 
     close(c1pipe2[READ]); 
     for(c = 0; c < 10; c++) 
     { 
      r = rand(); 
      //printf("C1 rand = %d\n", r%2); 
      if(r % 2 == 1) 
       writer(c1pipe2[WRITE], "1"); 
      else 
       writer(c1pipe2[WRITE], "0"); 
      sleep(1); 
     } 

     exit(0); 
    } 
    else if(C2 == 0) 
    { 
     close(c2pipe1[WRITE]); 
     close(c2pipe2[READ]); 
     for(c = 0; c < 10; c++) 
     { 
      r = rand(); 
      //printf("C2 rand = %d\n", r%2); 
      if(r % 2 == 1) 
       writer(c2pipe2[WRITE], "1"); 
      else 
       writer(c2pipe2[WRITE], "0"); 
      sleep(1); 
     } 

     exit(0); 
    } 
    else //parent 
    { 
     int buff1; //stores choice of c1 
     int buff2; //stores choice of c2 
     close(c1pipe1[READ]); 
     close(c1pipe2[WRITE]); 
     close(c2pipe1[READ]); 
     close(c2pipe2[WRITE]); 
     for(c = 0; c< 10; c++) 
     { 
      buff1 = atoi(reader(c1pipe2[READ])); 
      buff2 = atoi(reader(c2pipe2[READ])); 
      printf("C1's \(%d)\ choice trial %d : %d\n", C1, c+1, buff1); 
      printf("C2's \(%d)\ choice trial %d : %d\n", C2, c+1, buff2); 
      if(buff1 && buff2) //c1 and c2 cooperate with police 
      { 
        c1sentence = c1sentence + 6; 
        c2sentence = c2sentence + 6; 
      } 
      else if(buff1 || buff2) // one cooperates, one is silent 
      { 
       if(buff1) // if c1 cooperates and c2 is silent 
       { 
        c1sentence = c1sentence + 0; 
        c2sentence = c2sentence + 10; 
       } 
       else // if c2 cooperates and c1 is silent 
       { 
        c1sentence = c1sentence + 10; 
        c2sentence = c2sentence + 0; 
       } 
      } 
      else if(!(buff1 && buff2)) //both c1 and c2 are silent 
      { 
       c1sentence = c1sentence + 1; 
       c2sentence = c2sentence + 1; 
      } 
      sleep(1); 


     }  
     printf("C1 is in jail for %d years total\n", c1sentence); 
     printf("C2 is in jail for %d years total\n", c2sentence); 
     exit(0); 
    } 
    exit(0); 
} 

void writer(int pipe_write_fd, char *c) 
{ 
    open(pipe_write_fd); 
    char* choice = c; 
    // Write to the pipe 
    write(pipe_write_fd, choice, strlen(choice)); 
    // Close the pipe 
    // (Sends 'end of file' to reader) 
    close(pipe_write_fd); 
} 

char* reader(int pipe_read_fd) 
{ 
    open(pipe_read_fd); 
    // Allocate buffer to store 
    // result of read 
    int buffer_size = 1024; 
    char buffer[buffer_size]; 

    // Keep reading until we exhaust 
    // buffer or reach end of file 
    int i = 0; 
    while (i < buffer_size 
      && read(pipe_read_fd, &buffer[i], 1) > 0) 
    { i++; } 

    if (i < buffer_size) { 
     // Add null termination 
     buffer[i] = '\0'; 
    } else { 
     // We exhausted buffer 
     fprintf(stderr, "Warning: buffer full.\n"); 
     buffer[buffer_size-1] = '\0'; 
    } 

    //printf("%s", buffer); 

    // Close the pipe 
    close(pipe_read_fd); 
    return buffer; 
} 
+0

哈哈,這似乎是經典的「opertive系統」練習考試:D不是嗎? – dynamic 2011-02-12 23:38:50

+0

更多的作業,我不明白哈哈。我不會問,除了我花了很多時間在網上搜索答案/其他人,並且無法得到答案。 – Mike 2011-02-13 00:01:03

+0

添加作業標籤:) – 2011-04-16 15:35:55

回答

10

您需要關閉更多的管道。子進程必須關閉他們沒有使用的每個管道文件描述符。你有8個管道文件描述符;每個兒童進程必須關閉其中的6個 - 至少!你會很好地建議不要像前面那樣創建所有的管道 - 控制事情並使所有正確的描述符關閉是很複雜的。


的代碼更仔細,家長不寫郵件給子進程,所以你有兩倍多的管道,因爲你需要 - 你只需要一個管道爲每個子進程寫回父母與。

你也不會open()已經打開管道的文件描述符......但你是如何得到代碼編譯?您必須缺少正確的標頭(#include <fcntl.h>open(),並且沒有啓用足夠的警告選項進行編譯。

您提供的代碼中未使用您的變量COOP和SIL。


writer()功能不僅錯誤地試圖打開一個已經關閉的文件描述符,它也關閉它,這意味着沒有辦法後的第一個發回的額外信息。你應該只關閉一次完成的文件描述符 - 在每個孩子的主程序循環之後。這就是爲什麼你只看到一條消息。

還有一點值得養成糾正錯誤的習慣,那就是每次系統調用的返回都會失敗。有幾個不能失敗 - getpid()就是這樣。但是,I/O操作因程序的直接控制之外的原因(或者在這種情況下,在程序的控制之內)出於失敗的原因而臭名昭着,所以您應該檢查寫入是否成功。當你找回一個EBADF - 錯誤的文件描述符 - 錯誤時,你知道一些事情已經結束。

你必須在reader()close()(和open())類似的問題,再加上你試圖返回一個指向局部自動變量的另一個問題 - 這是不是一個好主意,直​​到永遠。同樣,啓用警告的體面編譯器(如GCC)會告訴您這些事情。我用這個命令編譯程序:

gcc -O -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \ 
    pipe.c -o pipe 

你的子進程總是會產生(僞)隨機數,這是不是很令人興奮的順序相同。你應該使用類似的東西:

srand(getpid()); 

確保他們得到不同的序列。


reader()功能都不夠熱情,太熱心讀取數據。您一次只讀取一個字節,但是隨後您將循環累積單個字節,因此代碼將等待所有10個結果被識別,然後立即將所有內容吐出。由於一個32位整數可以存儲一個最大爲1,111,111,111的數字而沒有問題,因此在第一次迭代時,您只需要撥打一個號碼返回atoi(),這不是您想要的。

對管道的讀寫操作是原子的 - 從某種意義上說,如果寫入過程寫入6個字節,而讀取過程試圖讀取6個以上的字節,則6個字節的數據包將由單次讀取返回,甚至如果管道中還有其他字節在等待讀取;這些額外的字節將在隨後調用read()時返回。

所以,你的reader()函數應該在一個緩衝區中傳遞,以及它的大小;代碼應該嘗試讀取該緩衝區大小;它應該無效終止它接收的內容;它可以將指針返回到它傳遞的緩衝區;它應該錯誤地檢查從read()返回的值。

兩個子進程的代碼基本相同 - 您應該使用適當的參數化函數,而不是將代碼寫出兩次。


全部放在一起,你最終是這樣的(這對我來說在MacOS X 10.6.6與GCC 4.5.2正常工作):

#include <errno.h> 
#include <string.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <stdarg.h> 
#include <stdlib.h> 

const int READ = 0; 
const int WRITE = 1; 

static char* reader(int fd, char *buffer, size_t bufsiz); 
static void writer(int fd, const char *c); 
static void child_process(int *my_pipe, int *his_pipe); 

static void err_exit(const char *fmt, ...) 
{ 
    va_list args; 
    int errnum = errno; 
    va_start(args, fmt); 
    vfprintf(stderr, fmt, args); 
    va_end(args); 
    if (errnum != 0) 
     fprintf(stderr, "%d: %s\n", errnum, strerror(errnum)); 
    exit(1); 
} 

int main(void) 
{  
    int c1pipe[2]; 
    int c2pipe[2]; 
    int c1sentence = 0; 
    int c2sentence = 0; 
    int c; 

    if (pipe(c1pipe) != 0 || pipe(c2pipe) != 0) 
     err_exit("Failed to open a pipe\n"); 

    int C2 = 0; 
    int C1 = fork(); 
    if (C1 > 0) 
     C2 = fork(); 

    if (C1 < 0 || C2 < 0) //error 
     err_exit("fork() failed\n"); 
    else if (C1 == 0) 
     child_process(c1pipe, c2pipe); 
    else if (C2 == 0) 
     child_process(c2pipe, c1pipe); 
    else //parent 
    { 
     int choice1; //stores choice of c1 
     int choice2; //stores choice of c2 
     char buffer1[BUFSIZ]; 
     char buffer2[BUFSIZ]; 
     close(c1pipe[WRITE]); 
     close(c2pipe[WRITE]); 
     for (c = 0; c< 10; c++) 
     { 
      choice1 = atoi(reader(c1pipe[READ], buffer1, sizeof(buffer1))); 
      choice2 = atoi(reader(c2pipe[READ], buffer2, sizeof(buffer1))); 
      printf("C1's (%d) choice trial %d : %d\n", C1, c+1, choice1); 
      printf("C2's (%d) choice trial %d : %d\n", C2, c+1, choice2); 
      if (choice1 && choice2) //c1 and c2 cooperate with police 
      { 
        c1sentence = c1sentence + 6; 
        c2sentence = c2sentence + 6; 
      } 
      else if (!(choice1 && choice2)) //both c1 and c2 are silent 
      { 
       c1sentence = c1sentence + 1; 
       c2sentence = c2sentence + 1; 
      } 
      else if (choice1) // if c1 cooperates and c2 is silent 
      { 
       c1sentence = c1sentence + 0; 
       c2sentence = c2sentence + 10; 
      } 
      else // if c2 cooperates and c1 is silent 
      { 
       c1sentence = c1sentence + 10; 
       c2sentence = c2sentence + 0; 
      } 
     }  
     printf("C1 is in jail for %d years total\n", c1sentence); 
     printf("C2 is in jail for %d years total\n", c2sentence); 
    } 
    return(0); 
} 

static void writer(int pipe_write_fd, const char *c) 
{ 
    int len = strlen(c); 
    if (write(pipe_write_fd, c, len) != len) 
     err_exit("Write failed\n"); 
} 

static char* reader(int pipe_read_fd, char *buffer, size_t bufsiz) 
{ 
    int i = read(pipe_read_fd, buffer, bufsiz-1); 
    if (i < 0) 
     err_exit("Read failed\n"); 
    buffer[i] = '\0'; 
    return buffer; 
} 

static void child_process(int *my_pipe, int *his_pipe) 
{ 
    int c; 
    srand(getpid()); 
    close(my_pipe[READ]); 
    close(his_pipe[READ]); 
    close(his_pipe[WRITE]); 
    for (c = 0; c < 10; c++) 
    { 
     writer(my_pipe[WRITE], ((rand() % 2) == 1) ? "1" : "0"); 
     sleep(1); 
    } 
    close(my_pipe[WRITE]); 
} 

注意如何錯誤例程儘早捕獲errno - 以避免損壞它。這是使用全局變量的危險之一;當你調用一個函數時它們可能會改變。避免使用它們時請勿使用它們(但請注意,一般而言,您無法完全避免使用errno)。

1
void writer(int pipe_write_fd, char *c) 
{ 
    open(pipe_write_fd); 
    char* choice = c; 
    // Write to the pipe 
    write(pipe_write_fd, choice, strlen(choice)); 
    // Close the pipe 
    // (Sends 'end of file' to reader) 
    close(pipe_write_fd); 
} 

我不知道哪個函數open您嘗試喲使用,但通常一個一個文件名,並返回一個文件描述符。在任何情況下,您都放棄了返回值,所以我認爲這並不重要。

什麼是明確的是你close第一次寫後立即管道,因此它是「正確的」,下一次寫入將失敗;管道已關閉。

如果你解決了這個問題,那麼接下來的問題是reader將在關閉讀取管道之前,一次一個字節,所有可用輸入 - 最多1024字節。當在循環中調用reader時,第二次迭代中的讀取嘗試將失敗。

0

你能複製你的打開和關閉功能的代碼嗎?正如查爾斯所說,他們似乎不是標準的。

此外,在函數readers()中,當您嘗試在主函數中讀取此變量的內容時,返回指向局部變量的指針是錯誤的,因此您沒有保證所指向的內存包含所需內容。如果你想返回一個緩衝區,你必須爲它保留malloc的內存,使用一個外部變量(我的意思是一個全局變量)或返回一個指向內部(本地)靜態變量的指針,因爲(不像自動本地變量),所有這些變量存儲在函數執行結束時都會持續存在。