[英]What happens if a child process won't close the pipe from writing, while reading?
給出以下代碼:
int main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
if (argc != 2) {
fprintf(stderr, "Usage: %s \n", argv[0]);
exit(EXIT_FAILURE);
}
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
return 0;
}
每當子進程想要從管道讀取時,它必須首先關閉管道側寫入。 當我刪除該行close(pipefd[1]);
從子進程的if
,我基本上說“好吧,孩子可以從管道讀取,但我允許父母同時寫入管道”?
如果是這樣,當管道打開以進行讀寫時會發生什么? 沒有相互排斥?
每當子進程想要從管道讀取時,它必須首先關閉管道側寫入。
如果進程 - 父進程或子進程 - 不打算使用管道的寫端,則應該關閉該文件描述符。 類似地,對於管道的讀取端。 系統將假定在任何進程打開寫入結束時可能發生寫入,即使唯一的此類進程是當前嘗試從管道讀取的進程,因此系統也不會報告EOF。 此外,如果您溢出管道並且仍然存在讀取結束打開的進程(即使該進程是嘗試寫入的進程),則寫入將掛起,等待讀取器為寫入完成留出空間。
當我刪除該行關閉(pipefd [1]); 從孩子的過程如果,我基本上說“好吧,孩子可以從管道讀取,但我允許父母同時寫入管道”?
沒有; 你說孩子可以寫信給管道和父母。 具有管道的寫文件描述符的任何進程都可以寫入管道。
如果是這樣,當管道打開讀寫時會發生什么 - 沒有相互排斥?
從來沒有任何相互排斥。 管道寫入描述符打開的任何進程都可以隨時寫入管道; 內核確保兩個並發寫操作實際上是序列化的。 打開管道讀取描述符的任何進程都可以隨時從管道讀取; 內核確保兩個並發讀操作獲得不同的數據字節。
通過確保只有一個進程打開以進行寫入並且只有一個進程打開以進行讀取,確保單向使用管道。 但是,這是一個編程決定。 你可以有N個進程,寫入結束打開,M進程讀取結束打開(並且,思想消失,N組和M組進程之間可能存在共同的進程),並且它們都能夠出乎意料地工作。 但是你不可能很容易地預測在寫入數據包之后將在何處讀取數據包。
fork()復制文件句柄,因此管道的每一端都有兩個句柄。
現在,考慮一下。 如果父級未關閉管道的未使用端,則仍會有兩個句柄。 如果孩子死了,孩子一側的把手就會消失,但父母仍然保持打開的把手 - 因此,管子仍然完全無效,所以永遠不會有“破管”或“EOF”到達。 沒有人再把數據放進去了。
當然,對於另一個方向也是如此。
是的,父母/孩子仍然可以使用句柄寫入自己的管道; 我不記得這個用例,它仍然給你同步問題。
創建管道時,它的兩端是讀端和寫端。 這些是用戶文件描述符表中的條目。
類似地,File表中將有兩個條目,其中1表示讀取結束和寫入結束的引用計數。
現在當你fork時,會創建一個子文件,文件描述符是重復的,因此文件表中兩端的引用計數變為2。
現在“當我刪除該行關閉(pipefd [1])” - >在這種情況下,即使父級已經完成寫入,此行下方的while循環將阻止讀取返回0(即EOF)。 發生這種情況,因為即使父進程已完成寫入並關閉管道的寫入結束,File表中寫入結尾的引用計數仍為1(最初為2),因此讀取函數仍在等待某些數據到達哪個永遠不會發生。
現在,如果你還沒有寫“close(pipefd [0]);” 在父級中,此當前代碼可能不會顯示任何問題,因為您在父級中編寫了一次。
但是如果你寫了不止一次,那么理想情況下你會想要得到一個錯誤(如果孩子不再讀),但由於父母的讀取結束沒有關閉,你將不會得到錯誤(即使孩子不再在那里閱讀)。
因此,當我們不斷讀/寫數據時,不關閉未使用端的問題變得明顯。 如果我們只是一次讀/寫數據,這可能不明顯。
就像在孩子中代替讀取循環一樣,你只使用下面的一行,在那里你一次性獲得所有數據,而不是關心檢查EOF,即使你不寫“你的程序也會工作”關閉(pipefd [1]);” 在孩子身上
read(pipefd[0], buf, sizeof(buf));//buf is a character array sufficiently large
用於SunOS的管道()的手冊頁: - 只讀一個空管(沒有緩沖數據)的讀取只有一端(所有寫文件描述符都關閉)返回一個EOF(文件結束)。
A SIGPIPE signal is generated if a write on a pipe with only
one end is attempted.
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.