我們知道標准C中的文件讀取的函數比如printf,fwrite等函數,實際都是調用OS級別的API,比如LINUX下就是wirte,read函數,而write read函數在用戶態下是沒有緩沖的, 當然在內核態有OS CACHE/OS BUFFER,所以某些直接調用wirte,read的程序肯定會分配一個緩沖區,特別是O_DIRECT這種方式下,內核態的OS CACHE和OS BUFFER沒用
這種情況下用戶態的BUFFER顯得更加重要,因為不可能一次讀一個字節吧,那性能可想而知而作為用戶態空間的STDIO也是這樣做的,它會為打開的文件分配緩存,默認應該是8192字節
如下圖摘自UNIX系統編程手冊 13章:
實際上我們可以使用setvbuf來設置某個打開文件的緩沖大小及模式。
我們來看看原型:
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
返回0為成功,非零為失敗
FILE *stream:打開文件的FILE*
char *buf:BUFFER的地址,如果為NULL,MODE為_IOLBF和_IOFBF則自動分配緩沖區
如果為_IONBF則不分配緩沖區
int mode:_IONBF不使用緩沖區,立即調用write/read,忽略buf和size為NULL和0,stderr屬於這個
_IOLBF使用行緩沖I/O,終端設備默認為這種。要麼遇到換行符要麼緩沖滿才調用write/read, stdin/stdout屬於這個。
_IOFBF采用全緩沖I/O,buffer滿才調用write/read,磁盤I/O屬於這個比如fwrite/fread
size_t size:緩沖大小
更簡單函數setbuf原型如下:
void setbuf(FILE *stream, char *buf);
相當於
setvbuf(fp,buf,(buf !=NULL)?_IOFBF:_IONBF,BUFSIZ);
如果buf=NULL則_IONBF打開不帶緩沖,或者調用全緩沖,BUFSIZ默認8192。
所以我們如果不想用緩沖直接:
setvbuf(fp,NULL)
即可。
現在回想一下fread
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
fread的時候我們通常要calloc一塊內存用於void *ptr,那麼現在我們想一下實際
上這個數據正常緩存到了3個地方
1、用戶分配的內存*ptr
2、STDIO的緩存默認8192
3、內核態OS CACHE
這視乎有點臃腫,我們可以想辦法簡化。事實上數據庫軟件有時候只使用了用戶態的一份緩存,而打開O_DIRECT來提高性能。
下面一個小程序可以驗證打開和關閉stdout緩沖的區別:
int main(void)
{
int i;
// setbuf(stdout,NULL);
for(i=0;i<10;i++)
{
printf("-");
sleep(1);
}
printf("\n");
}
區別就是是否使用setbuf,如果使用setbuf則 -符號會一個一個輸出,不使用會一起輸出
這就是STDIO緩存在作怪。