2015-08-08 38 views
0

我正在學習進程間通信,並且遇到了下面的示例程序。分叉和管道:這個C程序是否包含競態條件?

我不明白是爲了防止父進程與子進程之前嘗試的(如在程序底部的其他條件部分)已完成

在子進程寫入標準輸出之前,什麼(如果有的話)限制父進程嘗試從標準輸入讀取數據?

int main(void) 
{ 
    int  fd[2], nbytes; 
    pid_t childpid; 
    char string[] = "Hello, world!\n"; 
    char readbuffer[80]; 

    pipe(fd); 

    if((childpid = fork()) == -1) 
    { 
      perror("fork"); 
      exit(1); 
    } 

    if(childpid == 0) 
    { 
      /* Child process closes up input side of pipe */ 
      close(fd[0]); 

      /* Send "string" through the output side of pipe */ 
      write(fd[1], string, (strlen(string)+1)); 
      exit(0); 
    } 
    else 
    { 
      /* Parent process closes up output side of pipe */ 
      close(fd[1]); 

      /* Read in a string from the pipe */ 
      nbytes = read(fd[0], readbuffer, sizeof(readbuffer)); 
      printf("Received string: %s", readbuffer); 
    } 

    return(0); 
} 
+0

當調用系統函數'pipe()'時,總是檢查返回值以確保操作成功。成功時,pipe()在失敗時返回0,pipe()返回-1並設置'errno' – user3629249

+0

關於管道末端關閉的註釋向後 – user3629249

+0

'read()'函數不會追加字符串終止符字節' \ 0'讀取字符串,所以立即調用printf()(可能)會失敗。根據首先發生的是read()還是write(),讀取可能會在沒有讀取所有數據字節的情況下返回。讀取應該在循環中,同時檢查/累積返回的字節數。當返回的字節數爲0時,使用累加的字節數終止字符串(可能不需要,因爲子節點發送終止字節,但這是一個很好的做法)如果返回的值<0,則處理錯誤 – user3629249

回答

1

沒有什麼能阻止家長從孩子寫東西到管道之前開始read()電話,但父進程不會得到任何數據,直到孩子後已寫入的數據管道(和寫將是原子的,因爲它小於管道緩衝區的長度)。父母將等待一些數據到達管道或管道的每個寫入結束。

請注意,如果讀取後nbytes == 0printf()的輸出是不確定的,因爲readbuffer未初始化。

+0

謝謝 - 這引起了我另一個問題,這可能源於我的另一個誤解,但我會問 - 父母如何知道什麼時候沒有更多數據進入管道緩衝區?我正在考慮父母開始閱讀的情景,然後孩子寫下「Hello,world!」的「H」。 - 什麼是防止父母在讀完「H」之後立即進行假設,以確保沒有什麼可讀的內容? – sav0h

+0

當沒有更多數據要讀取並且沒有可寫入更多數據的進程(因此管道的所有寫入結束都關閉)時,讀取進程將返回0字節。內核處理它;讀取過程只需注意'read()'函數返回的值。 –

+0

@ sav0h只要一個或多個進程打開管道的寫通道,讀取過程(在您的代碼中,父代)將始終假定還有其他內容要讀取(並且將在'read(2)'中阻塞if目前沒有可用的東西)。請注意* Hello,world!*小於'PIPE_BUF',所以寫入是原子的(參見'man 7 pipe');假設傳遞給'read(2)'的緩衝區足夠長,一個'read(2)'調用就足夠了。 –