linux標準IO緩衝(apue)

2022-07-09 21:30:20 字數 4075 閱讀 7082

linux用緩衝的地方遍地可見,不管是硬體、核心還是應用程式,核心裡有頁高速緩衝,記憶體高速緩衝,硬體更不用說的l1,

l2 cache

,應用程式更是多的數不清,基本寫的好的軟體都有。但歸根結底這些緩衝的作用是相同的,都是為了提高機器或者程式的效能。而需要緩衝大部分的情況都是為了協調兩個裝置或者兩個系統間速度的不匹配。

大家都知道io

裝置的訪問速度與

cpu的速度相差好幾個數量級,所以為了協調

io裝置與

cpu的速度的不匹配,對於塊裝置核心使用了頁快取記憶體。也就是說,資料會先被拷貝到作業系統核心的頁快取區中,然後才會從作業系統核心的快取區拷貝到應用程式的位址空間。

當應用程式嘗試讀取某塊資料的時候,如果這塊資料已經存放在頁快取中,那麼這塊資料就可以立即返回給應用程式,而不需要經過實際的物理讀盤操作。當然,如果資料在應用程式讀取之前並未被存放在頁快取中,那麼就需要先將資料從磁碟讀到頁快取中去。對於寫操作來說,應用程式也會將資料先寫到頁快取中去,資料是否被立即寫到磁碟上去取決於應用程式所採用的寫操作機制:如果使用者採用的是同步寫機制,

那麼資料會立即被寫回到磁碟上,應用程式會一直等到資料被寫完為止;如果使用者採用的是延遲寫機制,那麼應用程式就完全不需要等到資料全部被 寫回到磁碟,資料只要被寫到頁快取中去就可以了。在延遲寫機制的情況下,作業系統會定期地將放在頁快取中的資料刷到磁碟上。與非同步寫機制不同的是,延遲寫機制在資料完全寫到磁碟上得時候不會通知應用程式,而非同步寫機制在資料完全寫到磁碟上得時候是會返回給應用程式的。所以延遲寫機制本省是存在資料丟失的風險的,而非同步寫機制則不會有這方面的擔心。

在應用程式中,我們經常也使用快取來解決io

裝置與cpu

速度的不匹配,如下面的

read

和write

函式:

1 ssize_t read(int filedes, void *buf, size_t nbytes);

2 ssize_t write(int filedes, const

void *buf, size_t nbytes);

我們呼叫read

函式從檔案讀取資料或者呼叫

write

來寫資料到檔案中,一般都是一次性的寫多個資料,很少一次寫乙個

byte

的資料(除了一些特殊的場景)。所以上面傳遞給

read

和write

的buf

引數其實就是我們的緩衝,這個緩衝的大小都是我們在寫程式的時候自己定義的。但問題就來了,我們應該定義多大的緩衝大小才能使

io效能達到最大呢?對於一些不了解核心或者檔案系統的人來說是很難知道這個大小的,下面是

apue

作者自己測試的不同緩衝大小對

io效能的影響的資料:

從上圖可以知道當緩衝達到4096

大小的時候,繼續增加緩衝大小對

io效能影響不大。這個

4096

大小由檔案系統的塊大小決定,由於上面的測試所用的檔案系統是

linux ext2

,塊大小是

4096

。標準io

庫則很好的解決了設定緩衝大小的問題,標準

io會選擇最佳的快取大小,使得我們不用再關心設定快取大小的問題,事實上標準

io庫會對每個

io流自動進行緩衝管理,從而避免了應用程式需要考慮這一點所帶來的麻煩。不過使用標準io

庫最大的問題也是它的緩衝,使用的不好很容易出現莫名其妙的結果。

在填滿標準io

緩衝區後才進行實際的

io操作。對於在磁碟上的檔案通常由標準

io庫實施全緩衝的。在乙個流上執行第一次i/o操作時,相關標準i/o函式通常呼叫malloc獲得需使用的快取區。

當在輸入和輸出中遇到換行符時,標準io

庫執行io

操作。這允許我們一次輸出乙個字元(用標準

io函式

fputc

),但只有在寫了一行之後才進行實際

io操作。通常涉及到終端(例如標準輸入和標準輸出)使用的是行緩衝。

對於行緩衝有兩個限制。第一,因為標準io

庫用來收集每一行的緩衝區的長度是固定的,所以只要填滿了緩衝區,那麼即使還沒有寫乙個換行符,也進行

io操作。第二,任何時候只要通過標準

io庫要求從(a

)乙個不帶快取的流,或者(

b)乙個行快取的流(它要求從核心得到資料)得到輸入資料,那麼就會造成沖洗所有行緩衝輸出流(因為後面讀取的資料可能就是前面輸出的資料)

。其實第二種情況我們會經常遇到,當我們先呼叫printf

輸出一串不帶換行符的字元時,執行完這條

printf

語句並不會立刻在螢幕中顯示我們輸出的資料,當我們接下來呼叫

scanf

從標準輸入讀取資料時,我們才看到前面輸出的資料。

標準io

庫不對字元進行儲存。例如,如果用標準

io函式

fputs寫15

個字元到不帶緩衝的流中,則該函式很可能直接呼叫

write

系統呼叫將這些字元立即寫到相關的檔案中。標準出錯流

stderr

是不帶緩衝的,這樣為了讓出錯的資訊可以盡快的顯示出來。

對於上面提到的每種檔案流,io

庫都預設分配乙個對應的緩衝給它,但有時候我們想自己設定這些緩衝,不要預設的,那麼我們可以使用下面兩個函式來達到目的:

1

void setbuf(file *restrict fp, char *restrict buf);

2void setvbuf(file *restrict fp, char *restrict buf, int mode, size_t size);

第乙個函式用來開啟或者關閉緩衝機制,如果buf

為null

,則關閉緩衝,否則

buf指向緩衝區,不過緩衝區的型別和檔案流有關。第二個函式可以精確的指定我們所需要的緩衝型別,如下圖所示。

下面我們編寫乙個小的程式來獲得標準io

預設緩衝的大小,**如下:

1 #include 2

3int stream_attribute(file *fp)

4else

if(fp->_flags &_io_line_buf)else

13 printf("

the io size : %d\n

",fp->_io_buf_end - fp->_io_buf_base);

14return0;

15}16int

main()

17

在ubuntu

下執行結果為:

下面修改下**,從鍵盤讀入一串字元並寫入到test

檔案中,**如下:

1 #include 2

3int stream_attribute(file *fp)

4else

if(fp->_flags &_io_line_buf)else

13 printf("

the io size : %d\n

",fp->_io_buf_end - fp->_io_buf_base);

14return0;

15}16int

main()

17

執行結果如下:

從上面的執行結果可以看出標準io

庫對流的緩衝不是在一開始就分配的,只有對流進行了輸入或者輸出才會實際的分配。在

linux

下,行緩衝的預設大小是

1k,全緩衝的大小預設是

4k,無緩衝的預設大小是

1位元組。

《apue

標準I O緩衝

ansi c裡定義的標準i o是一種帶緩衝的高階磁碟i o,目的是盡可能減少使用read和write系統呼叫的次數,從而提高i o效率.標準i o提供了3種型別的緩衝型別.全緩衝.在這種情況下,當填滿標準i o快取後才進行實際i o操作.對駐留在磁碟上的檔案的訪問通常是由標準i o庫實施全緩衝的.行...

標準IO緩衝

一 問題描述 終端無輸出結果。二 分析問題 1.標準輸出 stdout 是行緩衝模式。其何時會輸出在於 printf裡有 n fflush stdout 或setbuf stdout,null 緩衝區已滿三種。在應用程式退出時 exit 會ffush緩衝區。2.上述程式,如果將sleep 1 usl...

標準I O緩衝機制

標準i o庫提供緩衝的目的是盡可能減少使用read和write呼叫的次數。它對每個io流自動的進行緩衝管理,從而避免了應用程式需要考慮這一點所帶來的麻煩。不幸的是,標準io庫最令人迷惑的也是他的緩衝。標準io提供了3種緩衝機制 1 全緩衝。這種情況下,在填滿標準io緩衝區後才進行實際io操作。對於駐...