2012-02-29 121 views
4

所以我知道printf()是比write()更高的水平,並最終使用write()Printf()被緩衝,write()進行系統調用。爲什麼write()在輸出重定向的printf()之前打印?

實施例1,如果我是write()之前printf()運行一個程序然後它會的write()值之前輸出的printf()值。

示例2,如果我要運行相同的程序並使其通過輸出重定向到文件中,則在printf()之前輸出write()的值。

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

int main() 
{ 
    printf("This is a printf test\n"); 
    write(STDOUT_FILENO, "This is a write test\n", 21); 
    return 0; 
} 

我不明白這裏發生了什麼。在例1中,程序是否在運行前等待printf()的輸出write()?在示例2中,程序是否重定向了準備好的第一個輸出?並且因爲write()是更低級別,並且不需要像printf()那樣緩衝,那麼它首先被打印?

+3

查找['setvbuf()'](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setvbuf.html)以及'_IONBF','_IOLBF'和'_IOFBF'的含義。當輸出到達終端時,你會得到'_IOLBF'行爲;當輸出到一個文件時,你會得到'_IOFBF'行爲。 – 2012-02-29 00:13:55

回答

6

你回答了你自己的問題。

printf被緩衝,write不是。

對於輸出到終端,C stdio系統具有一個功能,只要它看到一個換行符'\n',就會刷新緩衝區。有關stdio緩衝的更多信息,請參閱setvbuf的文檔。

但是,當輸出到文件以提高速度時,stdio系統不刷新緩衝區。這就是爲什麼write輸出首先出現。

下面是一些strace輸出的從我的Linux系統上運行:

FSTAT(1,{ST_MODE = S_IFCHR | 0620,st_rdev = MAKEDEV(136,1),...})= 0
MMAP(NULL,4096,PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS,-1,0)= 0x7f7880b41000
寫(1 「這是一個printf測試\ n」 個,22)= 22
寫(1, 「This is a write test \ n \ 0」,22)= 22

fstat是stdio系統檢測輸出文件描述符1所連接的類型的位置。我相信它看起來在st_mode,並且看到它是一個字符設備。磁盤文件將是塊設備。 mmap是stdio緩衝區的內存分配,即4K。然後寫入輸出。

+0

我覺得不清楚。 Matteo的回答稱'通常libc所做的緩衝被禁用(或者每次回車都強制刷新)''。但是,你的答案是:「C stdio系統有一個功能,只要它看到一個換行符時就刷新緩衝區'\ n'。'printf總是進入緩衝區,對嗎?這只是在回車時是否被沖洗? – Strawberry 2012-02-29 00:30:31

+0

在輸出重定向的例子中,這是一個內存分配的東西嗎?這意味着在系統正在等待'printf'時,它會首先運行寫入權限?對於常規程序運行,在運行下一個命令之前是否檢查回車?我理解輸出的行爲,但它是如何深入研究的。我希望我能夠傳達我想要問的和理解的東西。 – Strawberry 2012-02-29 00:35:28

+1

@Doug:我相信大多數實現將輸出和緩衝區處理分開。所以'printf'對緩衝一無所知。它調用寫入緩衝區的內部函數。然後,如果它處於行緩衝模式,那麼這些函數將檢查緩衝區是否換行。如果他們找到一個,那麼該行將通過'write'發送到系統。如果它處於完全緩衝模式,它只會檢查一個完整的緩衝區,而不是換行符。 – 2012-02-29 00:46:59

0

當緩衝區已滿時,內部printf將使用write。如果它檢測到正在寫入交互式輸出(如尚未重定向的stdout),它也可能在緩衝區滿之前進行寫操作。

2

這與C標準庫完成的輸出緩衝有關。在第一種情況下,由於您正在終端上寫入,因此libc執行的緩衝是面向行的(每次回車都強制刷新),在屏幕上立即顯示文本,特權交互性(不應該成爲一個問題,因爲終端不會成爲大量文本的目標)。因此,printf輸出立即寫入一些write調用,這發生在您明確做出的下一個調用之前。

在第二種情況下,libc檢測到你正在寫一個文件,所以爲了提高性能,它啓用了緩衝;因此,通常第一個printf不會立即提交,並且您的write將發生在libc實際刷新緩衝區之前。

再次,這是通常發生。我不認爲這種行爲是由任何標準規定編輯:實際上,這是由C99強制,請參閱@喬納森的評論)(並在第二個例子中,即使啓用緩衝,庫可能無論如何決定做一個刷新,例如,如果緩衝區被你的printf填滿)。

+0

該行爲是由標準強制的。在C99,§7.19.3,¶3_When流被完全緩衝, 字符旨在被髮送到或者從主機環境時 一個緩衝器被填充的塊。當流線緩衝,字符旨在是 發送到或者從主機環境作爲塊當一個新行字符是 encountered._和¶7_As最初 打開時,標準錯誤流不完全緩衝;標準輸入和標準 輸出流當且僅當可以確定流不提及 到一個互動的device._ – 2012-02-29 00:18:21

+0

@JonathanLeffler是完全緩衝:呃,好知道,我沒有發現,因爲我一直在尋找爲「終端」而不是「交互設備」。我確定了我的答案。 – 2012-02-29 00:28:07

相關問題