[英]Child process not reading from pipe, unless parent calls printf before dup2
下面的代碼派生了一個子進程,並將標准輸出重定向到管道。 孩子應該從管道中讀取,但它沒有發生。 奇怪的是,如果讓父級在調用 dup2 之前至少調用一次 printf,那么一切似乎都有效。 我想這是一種不被依賴的運氣......但解釋仍然很好。 更重要的是,為什么孩子不識字?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
int fd[2];
pid_t p;
if(pipe(fd) == -1)
return -1;
if((p=fork()) == -1)
return -1;
if(p==0){
close(fd[1]);
dup2(fd[0],0);
fprintf(stderr,"Child starts\n");
int x;
scanf("%d",&x); // GETS STUCK HERE
fprintf(stderr,"Child ends\n");
exit(0);
}
// printf(" "); // THIS PRINTF SEEMS TO RESOLVE THE ISSUE?!
close(fd[0]);
dup2(fd[1],1);
printf("3\n");
fprintf(stderr, "Parent waiting\n");
wait(0);
fprintf(stderr, "Parent ends\n");
}
很多關於fork
和pipe
的問題都被問到了,但我找不到我的問題的答案。 抱歉,如果這是一個重復的問題。
父stdout
不是行緩沖的(因為它變成了管道)。 所以, printf("3\n");
的數據輸出保留在流的緩沖區中,不會刷新到管道中。
有兩種方法可以解決這個問題(在父級中):
setlinebuf(stdout);
就在那個printf
之前fflush(stdout);
緊接着printf
更新:
並添加額外的 printf() 修復它為什么? 我想那時,標准輸出的底層文件描述符仍然是一個終端,因此它將流置於行緩沖模式,並且當底層文件描述符更改為管道時,這不會改變。 ——喬納森·萊弗勒
對,那是正確的。 如果我們改變:
printf(" ");
進入:
setlinebuf(stdout);
然后,這也有效。
(即) printf
到一個真正的 tty [隱式] 設置行緩沖模式。
輸出的探測/測試是一個 tty 設備 [顯然] 推遲到設備完成某些活動(例如printf
)
dup2
對流是透明的,因此流的標志保持不變。
更新:
是否可以從孩子方面處理它?
不,孩子的記憶空間與父母不同。 它一開始是父母記憶的副本。 但是,它是獨立的。 對其進行的更改不會影響父母的記憶(反之亦然)。
更多關於這下面。 但是,請記住,孩子正在等待一個數字,該數字是一個數字字符串,后跟空格。 這里的空格是換行符。
例如,如果父 execv 是一個不同的程序,其輸出將由子程序讀取,該怎么辦? – 眼鏡蛇
我們必須小心術語。
“父/子”是fork
但不是execv
的概念。 因此,當在父母中執行execv
時,我們不是在談論孩子(或任何孩子)。 我們正在談論 exec 的“目標程序”。
目標程序與以前有相同的問題。
流是用戶空間/應用程序的概念。 操作系統內核不知道它們。 內核只知道“文件描述符”。 來自open
、 pipe
、 socket
等的東西。
stdio
流只是 I/O 的一種緩沖機制。 它們只存在於給定進程的內存中。 它們是一種“效率”機制,可防止對少量數據進行過多/重復/浪費的系統調用(即write
調用)。
“行緩沖”的概念只是流結構中的一個標志(以及它采取的行動)。 TTY 輸出設備默認為行緩沖——當看到換行時,緩沖區被刷新。
文件(或管道;-)等其他內容默認為“標准”緩沖(例如 4096 字節)。 當超過 4096 時,它們會被刷新。 “刷新”意味着stdio
層寫入底層文件描述符(通過write
系統調用)。
當一個execv
完成時,內存被完全替換並從目標程序的可執行文件內容中加載。
目標程序獲得控制權並運行其crt0.o
初始化代碼。 它必須從頭開始創建stdin/stdout/stderr
,而不考慮這些在execv
之前的內存空間中可能存在的內容(不再存在)
因此,當目標程序的main
函數獲得控制時, stdout
已附加到管道上。
它有同樣的緩沖問題。 事實上,之前完成的printf(" ")
對目標的stdout
流的狀態沒有影響。
目標永遠不會將stdout
(FD 1) 視為 TTY。 它已經被設置為管道(因此它得到標准緩沖)。 唯一的補救措施是使用setlinebuf
或定期進行fflush
調用。
如果該程序只是將數據輸出到stdout
然后終止,則stdout
流(如果目標甚至使用標准輸入輸出)在退出時被刷新。
否則,目標程序與原始程序具有相同的問題。
程序員應該知道他們正在處理一個管道(因為他們設置了它)並相應地處理緩沖。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.