12.20 流緩沖
通常情況下,寫入流的字符會在寫入前進行累積然后以塊的形式異步轉送而非由應用程序馬上輸出。相似的,流通常從主機環境以塊的形式而非字節-字節的形式讀入數據。這稱為緩沖。
如果你正在寫一個用流來交互的程序,當你設計交互接口時你需要理解緩沖是如何工作的。否則,你可能會發現輸出(例如進程提示信息)不輸出理想值,或者出現奇怪的行為。
這一節講解的是在流/文件/設備之間傳輸設備,並不會涉及echoing, flow control, 特殊設備。如果想了解關於終端設備的通用控制操作符的信息,參考Low-Level Terminal Interface。
你可以通過使用底層I/O函數與文件描述符來來避免使用流緩沖。參考 Low-Level I/O。
12.20.1 概念與術語
一共有三種緩沖策略:(譯者注:緩沖策略是寫入流/文件的充分條件,不是必要的。緩沖區存在的意義就是在使用“Stream-level I/O”時從緩沖區進行異步塊寫入/讀出,這樣可以在設備堵塞的時候或者有大量的寫操作時加快效率。如果一次只寫入少量數據,內核一看沒有堵塞,“干脆”就把緩沖區的內容寫入了,反正放着也是放着)
- 無緩沖 unbuffered :從一個無緩沖的流中讀寫會馬上產生效果
- 行緩沖 line buffered:當遇到一個換行符的時候字符會以塊的形式讀寫。
- 滿緩沖 fully buffered:字符會以任意大小的塊寫入讀出。(真的是直譯。。感覺和網上一些說滿的時候才讀寫的說法不一樣,說明可能是不堵塞的時候就讀寫緩沖區,最多等到緩沖區滿)
新開的流一般是滿緩沖的,只有一個例外:當流是一個可交互設備(例如終端)的時候,流將變為行緩沖。如果想了解關於如何選擇緩沖策略,參考 Controlling Buffering 。通常情況下,默認會選擇出最方便的緩沖策略。
在行緩沖下,以換行符結束的信息會馬上輸出到交互設備里——這通常是你想要的。不以換行符結尾的信息可能不會馬上顯示到交互設備,所以如果你想要立即顯示,你需要在寫入后使用fflush
, 參考 Flushing Buffers.(譯者注:通常使用fprintf+stderr,因為stderr默認是無緩沖的)
12.20.2 清除緩沖區
清除緩沖區意味着立即以塊的形式寫入緩沖區內收集的內容。有很多情況下緩沖區會自動清除:
- 當輸出緩沖區滿后嘗試輸出。
- 當流關閉的時候。參考 Closing Streams。
- 當程序通過調用
exit
結束的時候。參考 Normal Termination。 - 當向行緩沖緩沖區寫入換行符時。
- 當從本文件的任意流讀入時。
If you want to flush the buffered output at another time, call fflush
, which is declared in the header file stdio.h.如果你想要在別的時候清除緩沖區,可以使用stdio.h中聲明的fflush
。
-
Function: int fflush (FILE *stream)
Preliminary: | MT-Safe | AS-Unsafe corrupt | AC-Unsafe lock corrupt | See POSIX Safety Concepts.這個函數會導致與stream關聯的緩沖區被寫入到文件中,如果stream是一個空指針,那么
fflush
會導致目前打開的所有流的緩沖區被清除。這個函數將返回EOF
如果發生一個寫入錯誤,否則返回0。 -
Function: int fflush_unlocked (FILE *stream)
Preliminary: | MT-Safe race:stream | AS-Unsafe corrupt | AC-Unsafe corrupt | See POSIX Safety Concepts.
fflush_unlocked
函數和fflush
相同,除了它不會隱式的阻塞(block)這個stream流。
此處省略一些...
-
Function: void _flushlbf (void)
Preliminary: | MT-Safe | AS-Unsafe corrupt | AC-Unsafe lock corrupt | See POSIX Safety Concepts.該函數會清除所有的行緩沖流的緩沖區,聲明在stdio_ext.h頭文件中。
兼容性: 有一些腦子壞掉的操作系統總是對那些以換行符為導向的輸入“念念不忘”——清除一個行緩沖會導致一個換行符被寫入。幸運的是,這個特性越來越少見,並且對於GNU C Library 你不用擔心。
有一些情況下不去手動清除緩沖區而是忘記這件事可能是一個更好的選擇,因為讀寫可能不是必要的而且是相對花費大的。
-
Function: void __fpurge (FILE *stream)
Preliminary: | MT-Safe race:stream | AS-Unsafe corrupt | AC-Unsafe corrupt | See POSIX Safety Concepts.
__fpurge
函數會把stream流的緩沖區清空,但是不會產生讀寫!聲明在stdio_ext.h。
12.20.3 控制緩沖策略
在打開一個流之后,你通過使用setvbuf
選擇該流使用何種緩沖策略。
以下列出的函數和宏在頭文件stdio.h中聲明。
-
Function: int setvbuf *(FILE *stream, char buf, int mode, size_t size)
Preliminary: | MT-Safe | AS-Unsafe corrupt | AC-Unsafe lock corrupt | See POSIX Safety Concepts.這個函數是用來定義stream流該采用何種緩沖策略——可以是
_IOFBF
(滿緩沖),_IOLBF
(行緩沖), or_IONBF
(無緩沖)。如果你輸入的一個空指針作為buf參數,那么setvbuf
會自動使用malloc
申請一塊內存,當你關閉流的時候,緩沖區會被清除釋放掉。否則buf對應的內存塊至少應該是size大小。你不應該釋放掉buf對應的空間只要流還沒有關閉。你應該確保buf對應的內存是靜態存儲的(例如使用malloc
)。使用一個自動存儲期限的buf塊不是一個好的選擇——除非在退出當前塊之前關掉了流。當buf對應的數組塊是緩沖區的時候,stream流i/o函數會使用這個內存塊作為一些內部用途——所以你不應該試着去直接訪問這個數組的值當它被使用的時候。setvbuf
成功時返回0,否則返回非零數當mode是不可取的或者要求不能被滿足。 -
Macro: int _IOFBF
這個宏的值是一個整數常量表達式,可以被
setvbuf
函數用來是緩沖區是滿緩沖的。 -
Macro: int _IOLBF
這個宏的值是一個整數常量表達式,可以被
setvbuf
函數用來是緩沖區是行緩沖的。 -
Macro: int _IONBF
這個宏的值是一個整數常量表達式,可以被
setvbuf
函數用來是緩沖區是無緩沖的。 -
Macro: int BUFSIZ
這個宏的值是一個整數常量表達式,可以被
setvbuf
函數用來表達size,這個值被保證最小是256。BUFSIZ
是由操作系統選擇的,以此來提高i/o的效率。所以使用BUFSIZ
作為setvbuf
的大小值是一個很好的選擇。事實上,你可以用過fstat
系統調用獲得一個更好的值(在文件屬性的st_blksize
區域),參考 Attribute Meanings.有時候人們使用BUFSIZ
作為申請內存空間的大小值(或者作為i/o操作的內存,例如fgets)——這沒什么特別的理由,除了能提高一些i/o效率。 -
Function: void setbuf *(FILE *stream, char buf)
Preliminary: | MT-Safe | AS-Unsafe corrupt | AC-Unsafe lock corrupt | See POSIX Safety Concepts.If buf is a null pointer, the effect of this function is equivalent to calling
setvbuf
with a mode argument of_IONBF
. Otherwise, it is equivalent to callingsetvbuf
with buf, and a mode of_IOFBF
and a size argument ofBUFSIZ
.Thesetbuf
function is provided for compatibility with old code; usesetvbuf
in all new programs.如果buf是一個空指針,這個函數的效果等於setvbuf
使用_IONBF
.否則,等效於使用setvbuf
和_IOFBF
BUFSIZ
這兩個參數。該函數是為了兼容一些老的代碼,在新的程序中請使用setvbuf
. -
Function: void setbuffer *(FILE *stream, char buf, size_t size)
Preliminary: | MT-Safe | AS-Unsafe corrupt | AC-Unsafe lock corrupt | See POSIX Safety Concepts.
省略...這個函數是為了兼容一些BSD的老舊代碼。請使用
setvbuf
. -
Function: void setlinebuf (FILE *stream)
Preliminary: | MT-Safe | AS-Unsafe corrupt | AC-Unsafe lock corrupt | See POSIX Safety Concepts.
省略...這個函數是為了兼容一些BSD的老舊代碼。請使用
setvbuf
.剩下一些不常用的函數就不翻譯了。