2017-05-17 31 views
4

我很奇怪,爲什麼取消註釋下面的程序首先printf語句改變其後續行爲:標準輸入輸出端子收盤後(STDOUT_FILENO)行爲

#include <unistd.h> 
#include <stdio.h> 
#include <string.h> 
#include <errno.h> 

int main() { 
    //printf("hi from C \n"); 

    // Close underlying file descriptor: 
    close(STDOUT_FILENO); 

    if (write(STDOUT_FILENO, "Direct write\n", 13) != 13) // immediate error detected. 
    fprintf(stderr, "Error on write after close(STDOUT_FILENO): %s\n", strerror(errno)); 

    // printf() calls continue fine, ferror(stdout) = 0 (but no write to terminal): 
    int rtn; 
    if ((rtn = printf("printf after close(STDOUT_FILENO)\n")) < 0 || ferror(stdout)) 
    fprintf(stderr, "Error on printf after close(STDOUT_FILENO)\n"); 
    fprintf(stderr, "printf returned %d\n", rtn); 
    // Only on fflush is error detected: 
    if (fflush(stdout) || ferror(stdout)) 
    fprintf(stderr, "Error on fflush(stdout): %s\n", strerror(errno)); 
} 

如果沒有第一個printf,隨後printf的區域主題網絡34,如果沒有即使從stdout用戶緩衝區到基礎fd的連接已關閉,也會出現錯誤。只有在手動fflush(stdout)上纔會報告錯誤。 但是,隨着第一個printf打開,下一個printf報告錯誤,正如我所期望的那樣。 在兩種情況下STDOUT_FILENO fd都關閉後,終端(通過printf)沒有寫入任何內容。

我知道這是愚蠢的close(STDOUT_FILENO)在這裏擺在首位;這是一個我偶然發現的實驗,並且認爲某些在這些領域更有知識的人可能會看到我們在其中的一些啓發。

我在Linux上使用gcc。

+0

當你關閉底層描述符'STDOUT_FILENO'時,'stdout'處於不良狀態,''fclose(stdout)'正常工作。 –

回答

4

如果strace兩個方案,似乎stdio作品,使得在第一寫,它會檢查描述與fstat找出什麼樣的文件連接到stdout - 如果它是一個終端,那麼stdout應該是行緩衝,如果是別的東西,那麼stdout將被塊緩衝。如果在第一個printf之前調用close(1);,現在最初的fstat將返回EBADF,並且由於1不是指向字符設備的文件描述符,因此stdout被塊緩衝。

在我的電腦上,緩衝區大小爲8192字節 - 很多字節可以被緩衝寫入stdout之前會發生第一次故障。


如果取消對第一printf,所述fstat(1, ...)成功和Glibc檢測stdout連接到終端; stdout設置爲行緩衝,因此printf after close(STDOUT_FILENO)\n以換行符結束,緩衝區將立即刷新 - 這將導致立即出錯。

+0

奇怪的是,打印1024個字符會導致狀態更改......循環顯示printf可以很容易地顯示出來。 –

+1

是的,通過你描述的'fstat'調用內部緩衝區的設置似乎與'man setbuf'說的一致:「setvbuf()函數只能在打開一個流之後並且在其他任何操作之前使用執行它「。如果我用'setlinebuf(stdout)'替換第一個'printf';'它的行爲是一樣的。 –

+0

因此,一個類似的警告似乎適用於'printf',就像'man 2 write'中的註釋中出現的一樣:「write()的成功返回不能保證數據已被提交到磁盤。用'printf()'替換'write()',用'kernel file buffer'替換'disk'。這裏'fflush'和'fsync'是類似的。我不知道一個類似'close(fd)'的東西,它會切斷從內核文件緩衝區到磁盤的連接。 –