這篇博客簡單介紹了標準輸入/輸出相關的緩存機制。
標準輸入/輸出庫是由IOS C定義的,與直接使用系統(tǒng)調用進行讀、寫相比,標準輸入/輸
出幫助我們處理了很多細節(jié),比如:緩存分配、一次讀、寫的合適大小等。但標準輸入
輸出庫的緩存機制也會引發(fā)很多其他問題。
令人困惑的輸出....
printf是標準I/O庫函數函數之一,看下面一段程序
#include <stdio.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
printf("hello standard I/O");
sleep(5);
printf(" after 5 seconds\n");
sleep(5)
printf("after another 5 seconds\n");
}
上述程序很簡單,咋一看這個程序的運行過程應當是:
1. 輸出字符串hello standard I/O
2. 睡眠5秒鐘
3. 輸出字符串after 5 seconds
4. 睡眠5秒鐘
5. 輸出字符after anthor 5 seconds
6. 退出程序
然而,如果實際運行的過程是:
1. 睡眠5秒鐘
2. 輸出字符串hello standard I/O
3. 輸出字符串after 5 seconds
4. 睡眠5秒鐘
5. 輸出字符串after anthor 5 seconds
6. 退出程序
為什么會這樣呢?因為printf是有緩存區(qū)的,只有在特定情況(比如遇到換行符)下才會
刷新緩存區(qū),真正向屏幕輸出字符串,這就是為什么hello standard I/O并不會馬上輸出
,而是等5秒后和after 5 seconds一起輸出。
深入到printf緩存機制內部,我們重新解析上面程序運行過程,這時這段程序其實并不
像看起來那么簡單:
1. printf函數收到輸出字符串hello standard I/O的請求, 字符串先進入緩存區(qū),沒達到
刷新緩存區(qū)的條件,并不真正地輸出字符串
2. 睡眠5秒鐘
3. printf函數再次收到輸出字符串after 5 seconds\n的請求,字符串進入緩存區(qū),緩存區(qū)
的字符串包含換行符,調用操作系統(tǒng)調用,輸出緩存區(qū)內換行符前的所有字符串:
hello standard I/O after 5 seconds
4. 睡眠5秒鐘
5. printf函數收到輸出字符串after anthor 5 seconds\n的請求,字符串進入緩存區(qū),緩存
區(qū)的字符串包含換行符,調用操作系統(tǒng)調用,輸出緩存區(qū)內換行符前的所有字符串:
after anthor 5 seconds
緩存
絕大多數標準I/O函數都包含緩存,這意味著某些情況下,一個針對磁盤的寫請求只是更新了
緩沖區(qū),要等到滿足某些條件時,才會真正刷新緩沖區(qū)的變更到磁盤。緩存是為了減少系統(tǒng)
調用的次數,提高I/O效率,因為類似刷新緩存區(qū)到磁盤的操作需要系統(tǒng)調用,
而系統(tǒng)調用開銷比較大。
標準I/O函數中有3種類型的緩存
- 全緩存(fully buffered)
只有緩存區(qū)滿了的情況下,才會刷新緩存區(qū) - 行緩存(line buffered)
行緩存一般用于終端輸入和輸出
行緩存刷新緩存的情況有
2.1. 遇到換行符
2.2. 緩存區(qū)滿了
2.3. 輸入流是無緩存或者行緩存類型,輸入前,會刷新所有的行緩存類型的輸出流 - 無緩存(unbuffered)
更高效I/O
標準I/O中的緩存導致了數據不必要的重復拷貝,即數據先從內核拷貝到標準I/O的緩存區(qū),
然后數據從標準緩存區(qū)拷貝到用戶指定的地址中。有一些庫試圖通過減少這種拷貝來提高
I/O讀寫效率