簡體   English   中英

子進程不從管道讀取,除非父進程在 dup2 之前調用 printf

[英]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");
}

很多關於forkpipe的問題都被問到了,但我找不到我的問題的答案。 抱歉,如果這是一個重復的問題。

stdout不是行緩沖的(因為它變成了管道)。 所以, printf("3\n");的數據輸出保留在流的緩沖區中,不會刷新到管道中。

有兩種方法可以解決這個問題(在父級中):

  1. 添加setlinebuf(stdout); 就在那個printf之前
  2. 添加fflush(stdout); 緊接着printf

更新:

並添加額外的 printf() 修復它為什么? 我想那時,標准輸出的底層文件描述符仍然是一個終端,因此它將流置於行緩沖模式,並且當底層文件描述符更改為管道時,這不會改變。 ——喬納森·萊弗勒

對,那是正確的。 如果我們改變:

printf(" ");

進入:

setlinebuf(stdout);

然后,這也有效。

(即) printf到一個真正的 tty [隱式] 設置行緩沖模式。

輸出的探測/測試是一個 tty 設備 [顯然] 推遲到設備完成某些活動(例如printf

dup2對流是透明的,因此流的標志保持不變。


更新:

是否可以從孩子方面處理它?

不,孩子的記憶空間與父母不同。 它一開始是父母記憶的副本。 但是,它是獨立的。 對其進行的更改不會影響父母的記憶(反之亦然)。

更多關於這下面。 但是,請記住,孩子正在等待一個數字,該數字是一個數字字符串,后跟空格。 這里的空格是換行符。

例如,如果父 execv 是一個不同的程序,其輸出將由子程序讀取,該怎么辦? – 眼鏡蛇

我們必須小心術語。

“父/子”是fork不是execv的概念。 因此,當在父母中執行execv時,我們不是談論孩子(或任何孩子)。 我們正在談論 exec 的“目標程序”。

目標程序與以前有相同的問題。

流是用戶空間/應用程序的概念。 操作系統內核知道它們。 內核只知道“文件描述符”。 來自openpipesocket等的東西。

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM