關於OS Page Cache的簡單介紹

2022-07-07 14:06:18 字數 3117 閱讀 6254

在現代計算機系統中,cpu,ram,disk的速度不相同。cpu與ram之間,ram與disk之間的速度差異常常是指數級。為了在速度和容量上折中,在cpu與ram之間使用cpu cache以提高訪存速度,在ram與磁碟之間,作業系統使用page cache提高系統對檔案的訪問速度。

作業系統在處理檔案時,需要考慮兩個問題:

1.相對於記憶體的高速讀寫,緩慢的硬碟驅動器,特別是磁碟尋道較為耗時。

2.檔案載入到物理記憶體一次,並在多個程式間共享。

幸運的是,作業系統使用page cache機制解決了上面的兩個問題。page cache(頁面快取),核心在其中儲存頁面大小倍數的檔案塊。現假設一名為render的程式需要讀取512位元組scene.dat檔案的內容,流程分析如下:

1.render請求獲取512位元組scene.dat檔案的內容,使用系統呼叫 read(scene.dat, to_heap_buf, 512, offset=0)

2.核心從頁面快取中搜尋滿足請求的scene.dat檔案的4kb的塊,如果資料尚未快取,則進入下一步

3.核心申請頁幀空間,進行i/o操作,從偏移位置0開始請求4kb的資料,並複製到頁幀中

4.核心從page cache中複製512位元組的資料到render的快取中,read()系統呼叫結束

對於系統的所有檔案i/o請求,作業系統都是通過page cache機制實現的,對於作業系統而言,磁碟檔案都是由一系列的資料塊順序組成,資料塊的大小隨系統的不同而不同,x86 linux系統下是4kb(乙個標準的頁面大小)。核心在處理檔案i/o請求時,首先到page cache中查詢(page cache中的每乙個資料塊都設定了檔案以及偏移資訊),如果未命中,則啟動磁碟i/o,將磁碟檔案中的資料塊載入到page cache中的乙個空閒塊,之後copy到使用者緩衝區中。

在使用mmap呼叫時,系統並不馬上為其分配記憶體空間,而僅僅是新增乙個vma(virtual memory area)到該程序中,當程式訪問到目標空間時,產生缺頁中斷。在缺頁中斷中,從page cache中查詢要訪問的檔案塊,若未命中,則啟動磁碟i/o從磁碟中載入到page cache,然後將檔案塊在page cache中的物理頁對映到程序mmap位址空間。

當程式退出或關閉檔案時,系統是否會馬上清除page cache中的相應頁面呢?答案是否定的。由於該檔案可能被其它程序訪問,或該程序一段時間後會重新訪問,因此,在物理記憶體足夠的情況下,系統總是將其儲存在page cache中,這樣可以提高系統的整體效能。只有當系統物理記憶體不足時,核心才會主動清理page cache。

當程序呼叫write修改檔案時,由於page cache的存在,修改並不會馬上更新到磁碟,而只是暫時更新到page cache中,同時mark目標page為dirty,當核心主動釋放page cache時,才將更新寫入到磁碟(主動呼叫sync時,也會更新到磁碟)。

下面介紹一下kafka中對於page cache的利用。

kafka是一種高吞吐量的分布式發布訂閱訊息系統,有如下特性:

1.通過o(1)的磁碟資料結構提供訊息的持久化,這種結構對於即使數以tb的訊息儲存也能夠保持長時間的穩定性能

2.高吞吐量,即使是非常普通的硬體,kafka也可以支援每秒數百萬的訊息

3.支援kafka伺服器和消費機集群來分割槽訊息

4.支援hadoop並行資料載入

生產者寫入速度快,不同於redis和memcacheq等記憶體訊息佇列,kafka的設計是把所有的message都要寫入速度低,容量大的硬碟,以此來換取更強的儲存能力。實際上,kafka使用硬碟並沒有帶來過多的效能損失,而是「規規矩矩」的抄了一條近道。

首先說「規規矩矩」是因為kafka在磁碟上只做sequence i/o(順序寫),由於訊息系統讀寫的特殊性,這並不存在什麼問題。關於磁碟i/o的效能,kafka官方給出的測試資料(raid-5,7200rpm):

sequence i/o: 600mb/s (實驗室)

sequence i/o: 400mb-500mb/s (工作場景)

random i/o: 100kb/s

所以通過只做sequence i/o的限制,規避了磁碟訪問速度低下對效能可能造成的影響。

接下來談談kafka如何「抄近道」的。

首先,kafka重度依賴底層os提供的page cache功能。當上層有寫操作時,os只是將資料寫入到page cache,同時標記page屬性為dirty。當讀操作發生時,先從page cache中查詢,如果發生缺頁才進行磁碟排程,最終返回需要的資料。

實際上page cache是把盡可能多的空閒記憶體都當做磁碟快取來使用,同時如果有其它程序申請記憶體,**page cache的代價又很小,所以現代的os都支援page cache。使用page cache功能同時可以避免在jvm內部快取資料,jvm為我們提供了強大的gc功能,同時也引入了一些問題不適用與kafka的設計。如果在heap內管理快取,jvm的gc執行緒會頻繁掃瞄heap空間,帶來不必要的開銷。如果heap過大,執行一次full gc對系統的可用性來說將會是極大的挑戰。此外所有在jvm內的物件都不免帶有乙個object overhead(物件數量足夠多時,不可小覷此部分記憶體占用),記憶體的有效空間利用率會因此降低。所有in-process cache在os中都有乙份同樣的page cache。所以通過將快取只放在page cache,可以至少讓可用快取空間翻倍。如果kafka重啟,所有的in-process cache都會失效,而os管理的page cache依然可以繼續使用。

消費者獲取資料快,page cache還只是第一步,kafka為了進一步的優化效能還採用了sendfile技術。在解釋sendfile之前,先介紹一下傳統的網路i/o操作流程,大體上分為以下四步:

1.os從硬碟把資料讀到核心的page cache

2.使用者程序把資料從核心拷貝到使用者空間

3.然後使用者程序再把資料寫入到socket,資料流入核心空間的socket buffer上

4.os再把資料從buffer中拷貝到網絡卡到buffer上,這樣完成一次傳送

整個過程共經歷兩次context switch,四次system call。同乙份資料在核心buffer與使用者buffer之間重複拷貝,效率低下。其中2,3兩步沒有必要,完全可以直接在核心空間完成資料拷貝。這也是sendfile所解決的問題,經過sendfile優化後,整個i/o過程變成了下面的樣子:

關於apache HttpClient的簡單理解

1 httpentity用來封裝http body資料,不同型別的資料用不同型別的entity物件封裝,如stringentity用來封裝普通的json和xml資料,mulitpartentity用來封裝二進位製流資料,entity還有實體型別,實體長度等屬性。2 httprequest封裝應用層要...

關於Linux cached記憶體簡析

測試mbs系統,ap的weblogic 只是設定了1.5g記憶體,但是經過3次穩定性測試,記憶體的利用率達到99 nmon的監控揭發發現存在5.6g的cached記憶體,下面介紹一下 linux與windows不同,會存在快取記憶體,通常叫做cache memory。有些時候你會發現沒有什麼程式在執...

關於類模板語法的極簡示例

對c 的理解更深入了一點.自己實現了乙個簡單模板鍊錶 遇到的問題主要是對語法不清楚 但是有度娘和前人的探索還好.以下是我寫的極簡類模板完整示例 v.h pragma once templateclass v v.cpp include stdafx.h include v.h template v ...