1.C標準庫的I/O緩衝區
UNIX的傳統 是Everything is a file,鍵盤、顯示器、串口、磁碟等裝置在/dev 目錄下都有一個特殊的裝置檔案與之對應,這些裝置檔案也可以像普通檔案(儲存在磁碟上的檔案)一樣開啟、讀、寫和關閉,使用的函數介面是相同的。使用者程式調用C標準I/O庫函數讀寫普通檔案或裝置,而這些庫函數要通過系統調用把讀寫請求傳給核心 ,最終由核心驅動磁碟或裝置完成I/O操作。C標準庫為每個開啟的檔案分配一個I/O緩衝區以加速讀寫操作,通過檔案的FILE 結構體可以找到這個緩衝區,使用者調用讀寫函數大多數時候都在I/O緩衝區中讀寫,只有少數時候需要把讀寫請求傳給核心。以fgetc / fputc 為例,當使用者程式第一次調用fgetc 讀一個位元組時,fgetc 函數可能通過系統調用 進入核心讀1K位元組到I/O緩衝區中,然後返回I/O緩衝區中的第一個位元組給使用者,把讀寫位置指 向I/O緩衝區中的第二個字元,以後使用者再調fgetc ,就直接從I/O緩衝區中讀取,而不需要進核心 了,當使用者把這1K位元組都讀完之後,再次調用fgetc 時,fgetc 函數會再次進入核心讀1K位元組 到I/O緩衝區中。在這個情境中使用者程式、C標準庫和核心之間的關係就像在“Memory Hierarchy”中 CPU、Cache和記憶體之間的關係一樣,C標準庫之所以會從核心預讀一些資料放 在I/O緩衝區中,是希望使用者程式隨後要用到這些資料,C標準庫的I/O緩衝區也在使用者空間,直接 從使用者空間讀取資料比進核心讀資料要快得多。另一方面,使用者程式調用fputc 通常只是寫到I/O緩 沖區中,這樣fputc 函數可以很快地返回,如果I/O緩衝區寫滿了,fputc 就通過系統調用把I/O緩衝 區中的資料傳給核心,核心最終把資料寫回磁碟或裝置。有時候使用者程式希望把I/O緩衝區中的資料立刻 傳給核心,讓核心寫回裝置或磁碟,這稱為Flush操作,對應的庫函數是fflush,fclose函數在關閉檔案 之前也會做Flush操作。
我們知道main 函數被啟動代碼這樣調用:exit(main(argc, argv));。
main 函數return時啟動代碼會 調用exit ,exit 函數首先關閉所有尚未關閉的FILE *指標(關閉之前要做Flush操作),然後通 過_exit 系統調用進入核心退出當前進程.
C標準庫的I/O緩衝區有三種類型:全緩衝、行緩衝和無緩衝。當使用者程式調用庫函數做寫操作時, 不同類型的緩衝區具有不同特性。
全緩衝
如果緩衝區寫滿了就寫回核心。常規檔案通常是全緩衝的。
行緩衝
如果使用者程式寫的資料中有分行符號就把這一行寫回核心,或者如果緩衝區寫滿了就寫回內 核。標準輸入和標準輸出對應終端裝置時通常是行緩衝的。
無緩衝
使用者程式每次調庫函數做寫操作都要通過系統調用寫回核心。標準錯誤輸出通常是無緩衝的,這樣使用者程式產生的錯誤資訊可以儘快輸出到裝置。
除了寫滿緩衝區、寫入分行符號之外,行緩衝還有兩種情況會自動做Flush操作。如果:
使用者程式調用庫函數從無緩衝的檔案中讀取
或者從行緩衝的檔案中讀取,並且這次讀操作會引發系統調用從核心讀取資料
如果使用者程式不想完全依賴於自動的Flush操作,可以調fflush函數手動做Flush操作。
#include <stdio.h>
int fflush(FILE *stream);
傳回值:成功返回0,出錯返回EOF並設定errno
fflush函數用於確保資料寫回了核心,以免進程異常終止時遺失資料,如fflush(stdout); 作為一個特例,調 用fflush(NULL)可以對所有開啟檔案的I/O緩衝區做Flush操作。