IO之标準C庫buffer
在論述這個主題之前,先介紹一下标準C庫和linux系統調用以及windows API之間的關系。
拿寫檔案來舉個例子
linux下寫檔案用write()
windows下寫檔案用WriteFile()
這說明不同作業系統實作同樣的系統功能的接口應該是不一樣的。造成這種現狀是作業系統發展的曆史原因造成的,無法在作業系統的層面統一系統函數接口。同樣功能的程式在linux上寫一套,windows上又得寫另外一套,毫無移植性可言。如果要開發一個既能在linux跑,又能在windows上跑的程式,開發成本飙升!
為了解決這個移植性的問題,标準C庫利用了封裝技術,扮演了一個重要的角色,統一了部分基本功能接口。
标準C規定的寫檔案的函數是fwrite(),就是不管在linux還是在windows上,各自都有一個标準C庫,庫函數封裝的下層細節不一樣,但是接口完全一樣,提供的功能完全一樣。
這是怎麼做到的?猜一猜大緻實作就知道了
在linux上,标準C接口fwrite()的實作僞代碼
size_t fwrite(constvoid* buffer, size_t size, size_t count, FILE* stream){
...
...
return write(stream->fd,buffer,count);
}
在windows上,标準C接口fwrite()的實作僞代碼
size_t fwrite(constvoid* buffer, size_t size, size_t count, FILE* stream){
#defineOUT
BOOL ret = false;
OUT int optnum;
...
...
ret = WriteFile(stream->filehandle, buffer, count,&optnum,...);
if( ret == true)
return optnum;
else
return -1;
}
内部實作不一緻,沒關系,接口一樣就可以,不管在linux還是windows上,寫檔案都用fwrite(),分别在各自平台上編譯就可以了。
标準C(即應用層标準C語言函數庫)就是這樣一個處于系統層面之上的應用層标準函數庫,為了統一各個作業系統上的函數接口而生。
回到我們的主題----IO之應用層buffer
什麼是應用層buffer?
回想一下我之前介紹的《IO之核心buffer"buffer cache"》,既然write()能把需要寫檔案的資料推送到一個核心buffer來偷工減料欺騙應用層(為了加速I/O),說“我已經寫完檔案并傳回了”。那應用層的标準C庫的fwrite()按道理也可以為了加速,在真正調用write()之前,把資料放到(FILE*)stream->buffer中,等到多次調用fwrite(),直至(FILE*)stream->buffer中積攢的資料量達到(FILE*)stream->bufferlen這麼多的時候,一次性的把這些資料全部送入write()接口,寫入核心,這是多麼美妙啊。。。
實際上,标準C庫就是這麼做的!
把fwrite()的linux實作再細緻一下
過程其實仍然很粗糙,為了突出buffer的重點,計算stream->buffer是否滿,拷貝多少,填充多少這樣的細節和主題無關的東西我略去了
size_t fwrite(constvoid* buffer, size_t size, size_t count, FILE* stream){
...
if( stream->buffer滿 ){
write(stream->fd,stream->buffer,stream->bufferlen);
} else{
拷貝buffer内容至stream->buffer//應用層的C庫裡的buffer指的就是這裡的stream->buffer
}
...
return count;
//過程很粗糙,為了突出buffer的重點,計算stream->buffer是否滿,拷貝多少,填充多少這樣的細節和主題無關的東西我略去了
}
fwrite()在windows平台的實作也基本上是這樣的,也有buffer。
值得一說的是,fread()也有一個讀cache來完成預讀。
setvbuf()和setbuf()都是控制這個标準C庫的buffer的。
還有fflush()是C庫用于flush(應用層buffer,即這個标準C庫裡的buffer)資料的函數。
以上三個函數,如果大家有興趣,可以去看看linux上對應的man文檔。
重點是要知道不僅系統的核心有buffer,應用層的C庫同樣也有buffer。這些buffer的唯一作用就是為了加速應用,不讓應用老是卡在和磁盤互動上。
說個題外話,
實際上對于磁盤、RAID卡、盤陣這樣的外存媒體而言,他們各自在硬體上也都有一層前端的buffer,有時也叫cache,用來緩沖讀寫加速。cache越多,價格越貴,性能越好。大型儲存設備一般擁有多層cache,用的是昂貴的SSD。
需要分享的一點經驗是,不管是标準C庫的buffer也好,核心的"buffer cache"也罷,我們終究對它們的控制力度是有限的。我們在做伺服器程式的時候,如果業務上涉及太大的I/O量,需要做服務整體加速的時候,我們一般自己在業務層做一層自己的"buffer",把業務資料buffer住,攢成以檔案系統或者磁盤的block塊機關的大塊資料,然後集中寫,然後集中寫又有集中寫的政策。。。
再引申一點内容,做高性能大流量的大站的架構,其中最重要幾個架構角色之一就是cache。前端CDN、後端memcache、redis、mysql内部cache等等,都是cache的應用場景,可以說"buffer cache"在伺服器領域從軟體實作到硬體加速再到架構,真的是無處不在。