1. 程式人生 > >Linux Page Cache機制

Linux Page Cache機制

經過研究了下Linux相關程式碼,把對Linux Cache實現的方式做一些總結。
相關原始碼主要在:
./fs/fscache/cache.c    Cache實現的程式碼
./mm/slab.c                   SLAB管理器程式碼
./mm/swap.c                快取替換演算法程式碼
./mm/mmap.c             記憶體管理器程式碼
./mm/mempool.c       記憶體池實現程式碼

0. 預備:Linux記憶體管理基礎

建立程序fork()、程式載入execve()、對映檔案mmap()、動態記憶體分配malloc()/brk()等程序相關操作都需要分配記憶體給程序。不過這時程序申請和獲得的還不是實際記憶體,而是虛擬記憶體,準確的說是“記憶體區域”。Linux除了核心以外,App都不能直接使用記憶體,因為Linux採用Memory Map的管理方式,App拿到的全部是核心對映自實體記憶體的一塊虛擬記憶體。malloc分配很少會失敗,因為malloc只是通知記憶體App需要記憶體,在沒有正式使用之前,這段記憶體其實只在真正開始使用的時候才分配,所以malloc成功了並不代表使用的時候就真的可以拿到這麼多記憶體。據說Google的tcmalloc改進了這一點。

程序對記憶體區域的分配最終多會歸結到do_mmap()函式上來(brk呼叫被單獨以系統呼叫實現,不用do_mmap())。核心使用do_mmap()函式建立一個新的線性地址區間,如果建立的地址區間和一個已經存在的地址區間相鄰,並且它們具有相同的訪問許可權的話,那麼兩個區間將合併為一個。如果不能合併,那麼就確實需要建立一個新的VMA了。但無論哪種情況, do_mmap()函式都會將一個地址區間加入到程序的地址空間中,無論是擴充套件已存在的記憶體區域還是建立一個新的區域。同樣釋放一個記憶體區域使用函式do_ummap(),它會銷燬對應的記憶體區域。

另一個重要的部分是SLAB分配器。在Linux中以頁為最小單位分配記憶體對於核心管理系統實體記憶體來說是比較方便的,但核心自身最常使用的記憶體卻往往是很小(遠遠小於一頁)的記憶體塊,因為大都是一些描述符。一個整頁中可以聚集多個這種這些小塊記憶體,如果一樣按頁分配,那麼會被頻繁的建立/銷燬,開始是非常大的。

為了滿足核心對這種小記憶體塊的需要,Linux系統採用了SLAB分配器。Slab分配器的實現相當複雜,但原理不難,其核心思想就是Memory Pool。記憶體片段(小塊記憶體)被看作物件,當被使用完後,並不直接釋放而是被快取到Memory Pool裡,留做下次使用,這就避免了頻繁建立與銷燬物件所帶來的額外負載。

Slab技術不但避免了記憶體內部分片帶來的不便,而且可以很好利用硬體快取提高訪問速度。但Slab仍然是建立在頁面基礎之上,Slab將頁面分成眾多小記憶體塊以供分配,Slab中的物件分配和銷燬使用kmem_cache_alloc與kmem_cache_free。

1. Linux Cache的體系

在 Linux 中,當App需要讀取Disk檔案中的資料時,Linux先分配一些記憶體,將資料從Disk讀入到這些記憶體中,然後再將資料傳給App。當需要往檔案中寫資料時,Linux先分配記憶體接收使用者資料,然後再將資料從記憶體寫到Disk上。Linux Cache 管理指的就是對這些由Linux分配,並用來儲存檔案資料的記憶體的管理。

下圖描述了 Linux 中檔案 Cache 管理與記憶體管理以及檔案系統的關係。從圖中可以看到,在 Linux 中,具體的檔案系統,如 ext2/ext3/ext4 等,負責在檔案 Cache和儲存裝置之間交換資料,位於具體檔案系統之上的虛擬檔案系統VFS負責在應用程式和檔案 Cache 之間通過 read/write 等介面交換資料,而記憶體管理系統負責檔案 Cache 的分配和回收,同時虛擬記憶體管理系統(VMM)則允許應用程式和檔案 Cache 之間通過 memory map的方式交換資料,FS Cache底層通過SLAB管理器來管理記憶體。

Linux Cache體系 1
下圖則非常清晰的描述了Cache所在的位置,磁碟與VFS之間的紐帶。

Linux Cache體系 2

2. Linux Cache的結構

在 Linux 中,檔案 Cache 分為兩層,一是 Page Cache,另一個 Buffer Cache,每一個 Page Cache 包含若干 Buffer Cache。記憶體管理系統和 VFS 只與 Page Cache 互動,記憶體管理系統負責維護每項 Page Cache 的分配和回收,同時在使用 memory map 方式訪問時負責建立對映;VFS 負責 Page Cache 與使用者空間的資料交換。而具體檔案系統則一般只與 Buffer Cache 互動,它們負責在外圍儲存裝置和 Buffer Cache 之間交換資料。讀快取以Page Cache為單位,每次讀取若干個Page Cache,回寫磁碟以Buffer Cache為單位,每次回寫若干個Buffer Cache。
Page Cache、Buffer Cache、檔案以及磁碟之間的關係如下圖所示。

Linux Cache實現

Page 結構和 buffer_head 資料結構的關係如下圖所示。Page指向一組Buffer的頭指標,Buffer的頭指標指向磁碟塊。在這兩個圖中,假定了 Page 的大小是 4K,磁碟塊的大小是 1K。

Page Cache結構

在 Linux 核心中,檔案的每個資料塊最多隻能對應一個 Page Cache 項,它通過兩個資料結構來管理這些 Cache 項,一個是 Radix Tree,另一個是雙向連結串列。Radix Tree 是一種搜尋樹,Linux 核心利用這個資料結構來通過檔案內偏移快速定位 Cache 項,圖 4 是 radix tree的一個示意圖,該 radix tree 的分叉為4(22),樹高為4,用來快速定位8位檔案內偏移。Linux(2.6.7) 核心中的分叉為 64(26),樹高為 6(64位系統)或者 11(32位系統),用來快速定位 32 位或者 64 位偏移,Radix tree 中的每一個到葉子節點的路徑上的Key所拼接起來的字串都是一個地址,指向檔案內相應偏移所對應的Cache項。

Page Cache使用的Radix Tree 1

檢視Page Cache的核心資料結構struct address_space就可以看到上述結構(略去了無關結構):

struct address_space  {

struct inode             *host;/* owner: inode, block_device */

struct radix_tree_root      page_tree;/* radix tree of all pages */

unsignedlong           nrpages;/*number of total pages */

struct address_space       *assoc_mapping;/* ditto */

......

} __attribute__((aligned(sizeof(long))));

下面是一個Radix Tree例項:

Page Cache使用的Radix Tree 2

另一個數據結構是雙向連結串列,Linux核心為每一片實體記憶體區域(zone) 維護active_list和inactive_list兩個雙向連結串列,這兩個list主要用來實現實體記憶體的回收。這兩個連結串列上除了檔案Cache之 外,還包括其它匿名(Anonymous)記憶體,如程序堆疊等。

Linux Cache 置換演算法

相關資料結構如下:

struct page{

struct list_head list;//通過使用它進入下面的資料結構free_area_struct結構中的雙向鏈佇列

struct address_space * mapping;//用於記憶體交換的資料結構

unsignedlong index;//當頁面進入交換檔案後

struct page *next_hash;//自身的指標,這樣就可以連結成一個連結串列

    atomic t count;//用於頁面交換的計數,若頁面為空閒則為0,分配就賦值1,沒建立或恢復一次對映就加1,斷開對映就減一

unsignedlong flags;//反應頁面各種狀態,例如活躍,不活躍髒,不活躍乾淨,空閒

struct list_head lru;

unsignedlong age;//表示頁面壽命

   wait_queue_head_t wait;

struct page ** pprev_hash;

struct buffer_head * buffers;

void*virtual

struct zone_struct * zone;//指向所屬的管理區

}

typedefstruct free_area_struct {

struct list_head free_list;//linux中通用的雙向鏈佇列

unsignedint* map;

} free_area_t;

typedefstruct zone_struct{

    spinlock_t        lock;

unsignedlong offset;//表示該管理區在mem-map陣列中,起始的頁號

unsignedlongfree pages;

unsignedlong inactive_clean_pages;

unsignedlong inactive_dirty_pages;

unsigned pages_min, pages_low, pages_high;

struct list_head inactive_clean_list;//用於頁面交換的佇列,基於linux頁面交換的機制。這裡存貯的是不活動乾淨頁面

    free_area_t free_area[MAX_ORDER];//一組空閒區間佇列,free_area_t定義在上面,其中空閒下標表示的是頁面大小,例如:陣列第一個元素0號,表示所有區間大小為2 0次方的頁面連結成的雙向佇列,1號表示所有21次方頁面連結連結成的雙向佇列,2號表示所有22次方頁面連結成的佇列,其中要求是這些頁面地址連續

char* name;

unsignedlong size;

struct pglist_data * zone_pgdat;//用於指向它所屬的存貯節點,及下面的資料結構

unsignedlong  zone_start_paddr;

unsignedlong    zone_start_mapnr;

struct page * zone_mem_map;

} zone_t;



3. Cache預讀與換出

Linux 核心中檔案預讀演算法的具體過程是這樣的:
對於每個檔案的第一個讀請求,系統讀入所請求的頁面並讀入緊隨其後的少數幾個頁面(不少於一個頁面,通常是三個頁 面),這時的預讀稱為同步預讀。對於第二次讀請求,如果所讀頁面不在Cache中,即不在前次預讀的group中,則表明檔案訪問不是順序訪問,系統繼續 採用同步預讀;如果所讀頁面在Cache中,則表明前次預讀命中,作業系統把預讀group擴大一倍,並讓底層檔案系統讀入group中剩下尚不在 Cache中的檔案資料塊,這時的預讀稱為非同步預讀。無論第二次讀請求是否命中,系統都要更新當前預讀group的大小。
此外,系統中定義了一個 window,它包括前一次預讀的group和本次預讀的group。任何接下來的讀請求都會處於兩種情況之一:
第一種情況是所請求的頁面處於預讀 window中,這時繼續進行非同步預讀並更新相應的window和group;
第二種情況是所請求的頁面處於預讀window之外,這時系統就要進行同步 預讀並重置相應的window和group。
下圖是Linux核心預讀機制的一個示意圖,其中a是某次讀操作之前的情況,b是讀操作所請求頁面不在 window中的情況,而c是讀操作所請求頁面在window中的情況。

Cache預讀演算法

Linux核心中檔案Cache替換的具體過程是這樣的:剛剛分配的Cache項鍊入到inactive_list頭部,並將其狀態設定為active,當記憶體不夠需要回收Cache時,系統首先從尾部開始反向掃描 active_list並將狀態不是referenced的項鍊入到inactive_list的頭部,然後系統反向掃描inactive_list,如果所掃描的項的處於合適的狀態就回收該項,直到回收了足夠數目的Cache項。其中Active_list的含義是熱訪問資料,及多次被訪問的,inactive_list是冷訪問資料,表示尚未被訪問的。如果資料被訪問了,Page會被打上一個Refrence標記,如果Page沒有被訪問過,則打上Unrefrence標記。這些處理在swap.c中可以找到。
下圖也描述了這個過程。

Linux Cache 置換演算法

下面的程式碼描述了一個Page被訪問它的標記為變化:

*

* Mark a pageas having seen activity.

*

*inactive,unreferenced        ->      inactive,referenced

*inactive,referenced          ->      active,unreferenced

*active,unreferenced          ->      active,referenced

*/

void mark_page_accessed(struct page *page)

{

if(!PageActive(page)&&!PageUnevictable(page)&&

                       PageReferenced(page)&& PageLRU(page)){

               activate_page(page);

               ClearPageReferenced(page);

}elseif(!PageReferenced(page)){

               SetPageReferenced(page);

}

}

相關推薦

Linux Page Cache機制

經過研究了下Linux相關程式碼,把對Linux Cache實現的方式做一些總結。 相關原始碼主要在: ./fs/fscache/cache.c    Cache實現的程式碼 ./mm/slab.c                   SLAB管理器程式碼 ./mm/s

linux Page cache和buffer cache正解

Page cache和buffer cache一直以來是兩個比較容易混淆的概念,在網上也有很多人在爭辯和猜想這兩個cache到底有什麼區別,討論到最後也一直沒有一個統一和正確的結論,在我工作的這一段時間,page cache和buffer cache的概念曾經困擾過我,但是仔細分析一下,這兩個概念實際上非常的

Linux Page cache和buffer cache深入理解

Page cache和buffer cache一直以來是兩個比較容易混淆的概念,在網上也有很多人在爭辯和猜想這兩個cache到底有什麼區別,討論到最後也一直沒有一個統一和正確的結論,在我工作的這一段時間,page cache和buffer cache的概念曾經困擾過我,但是仔細分析一下,這兩個概念實際上非常的

Linux Page cache和Block I/O layer

下面內容是來自LKD和ULK的讀書筆記,見該書的LKD的《Chapter 16 The Page Cache and Page Writeback》和《Chapter 14 The Block I/O Layer》,以及ULK的《Chapter 18 The Ext2 an

作業系統中的page cache機制

在現代計算機系統中,CPU,RAM,DISK的速度不相同,按速度高低排列為:CPU>RAM>DISK。CPU與RAM之間、RAM與DISK之間的速度差異常常是指數級。同時,它們之間的處理容量也不相同,其差異也是指數級。為了在速度和容量上折中,在CPU與RAM之間

linux page buffer cache深入理解

返回 edit 占用率 prev click tco 存儲 多重 tid   Linux上free命令的輸出。   下面是free的運行結果,一共有4行。為了方便說明,我加上了列號。這樣可以把free的輸出看成一個二維數組FO(Free Output)。例如: FO[2]

LinuxCache Memory(緩存內存)機制

read linux系統 編程 d+ 以及 需求 部分 int ted 轉:https://blog.csdn.net/kaikai_sk/article/details/79177036 PS:為什麽Linux系統沒運行多少程序,顯示的可用內存這麽少?其實Linux與Wi

Linux核心分析筆記----Page CachePage Writeback

頁快取記憶體是linux核心實現的一種主要磁碟快取,它主要用來減少對磁碟的IO操作,具體地講,是通過把磁碟中的資料快取到實體記憶體中,把對磁碟的訪問變為對實體記憶體的訪問。為什麼要這麼做呢?一,速度;二臨時區域性原理。有關這兩個概念,相信熟悉作業系統的我們不會太陌生。頁快取記憶體是由RAM中的物理頁組

linux cache 機制

在閱讀文章前,您應該具備基本的儲存器層次結構知識,至少要了解區域性性原理。要詳細瞭解cache基本原理,可以參考本書《深入理解計算機系統》中儲存器體系結構一章:   帶著疑問來看文章,cache對於程式設計師是不可見的,它完全是由硬體控制的,為什麼在linux核心中還有cache.h這個標頭檔案,定

Linuxpage cache使用情況/命中率檢視和操控

這裡總結幾個Linux檔案快取(page cache)使用情況、命中率檢視的工具。perf-tools裡面的cachestat來自於大名鼎鼎的Brendan Gregg的cachestat,已經被加到他的perf-toolshttp://www.brendangregg.co

JaVa中常用緩存CaCHE機制

ava lis hao123 java 緩存cache 機制 http .com list %E5%85%B3%E4%BA%8E%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0%E5%8F%82%E6%95%B0%E7%9A%84%E9%97%AE%

linux RCU鎖機制分析

nbsp -i html 都在 而且 content 服務器 單詞 插入 openVswitch(OVS)源代碼之linux RCU鎖機制分析 分類: linux內核 | 標簽: 雲計算,openVswitch,linux內核,RCU鎖機制 | 作者: yuzh

使用go語言繞過page cache讀寫文件

tdi port 否則 測試程序 ++ cal creation slice def 有時候,我們希望我們的數據不通過page cache的緩沖直接落盤。go語言裏,用參數DIRECT打開文件可以實現這一點要求。 但這樣做有一個硬性的要求,就是在讀寫的時候,對應的數據在內存

LinuxCache內存占用過高解決辦法

格式化 left ack 當前 區別 專業 技術分享 表示 進行 在Linux系統中,我們經常用free命令來查看系統內存的使用狀態。在一個RHEL6的系統上,free命令的顯示內容大概是這樣一個狀態: 這裏的默認顯示單位是kb,我的服務器是128G內存,所以數字顯得

linux apt-cache使用方法

def auto con it is play only indicate eth html apt-cache是linux下的一個apt軟件包管理工具,它可查詢apt的二進制軟件包緩存文件。APT包管理的大多數信息查詢功能都可以由apt-cache命令實現,通過apt-c

Linux內存機制以及手動釋放swap和內存

深入 drop png 釋放 href amr 保持 釋放內存 內存大小 今天我們來談談Linux的內存機制。 首先我們理一下概念 一、什麽是linux的內存機制? 我們知道,直接從物理內存讀寫數據要比從硬盤讀寫數據要快的多,因此,我們希望所有數據的讀取和寫入都在內存完成,

Linux 的命令機制

Linux之路命令格式 COMMAND [OPTIONS...] [ARGUMENTS...]選項:用於啟用或關閉命令的某個或某些功能短選項:-c 例如:-l, -h長選項:--word 例如:--all, --human-readable參數:命令的作用對象: 比如文件名和作用名 註意 1. *多個選項以

釋放Ubuntu/Linux系統cache,增加可用內存空間

free cache查看內存使用命令 free -m watch -n 1 cat /proc/meminfo 釋放內存命令 釋放kernel用在cache上面的內存 sudo sysctl -w vm.drop_caches=3 釋放無論是使用中的內存還是cache(page cache, ino

Linux之poll機制分析

for 可用 報告 超時時間 程序 訪問 events blank linux 應用程序訪問1個設備文件時可用阻塞/非阻塞方式.如果是使用阻塞方式,則直接調用open()、read()、write(),但是在驅動程序層會判斷是否可讀/可寫,如果不可讀/不可寫,則將當前進程休

docker build 的 cache 機制

包括 鏡像 存在 沒有 pretty -- 安裝、配置 nbsp bsp cache 機制註意事項 可以說,cache 機制很大程度上做到了鏡像的復用,降低存儲空間的同時,還大大縮短了構建時間。然而,不得不說的是,想要用好 cache 機制,那就必須了解利用 cache 機