什麼是 Page Cache
1 什麼是 Page Cache
Page Cache,翻譯爲頁高速緩衝存儲器。它是動態變化的,因爲操作系統會將所有未直接分配給應用程序的物理內存都用於頁面緩存。Page Cache 是文件系統層級的緩存,用於緩存文件的頁數據,屬於內核管理的內存。從磁盤中讀取到的內容是存儲在 page cache 裏的。
2 爲什麼需要 Page Cache
Page Cache 機制的目的是爲了減少 IO,提升 IO 磁盤讀寫的效率。由於程序的時間局部性和空間局部性,讀寫過的文件在下次還可能再次讀取,如果每次讀寫文件都去磁盤中獲取,顯然讀寫性能太差,因爲磁盤的讀寫速率相對於內存來說,慢了不止一點點。
因爲有 Page Cache 機制,所以我們可以發現讀寫一個文件第一次非常慢,但是第二次就會變得很快,這是因爲第一次讀寫這個文件的時候,Linux 內核已經把文件內容緩存到了內存中的 Page Cache 裏面,第二次讀寫的時候,由於發現文件內容已經在內存中了,就直接從內存中讀取了,這顯然比從硬盤讀取快很多。
Page Cache 的機制是很複雜的,那我們可不可以不用 Page Cache 呢?
答案當然是可以的,我們可以在應用層實現自己的類似這種的 Cache 機制,比如 MySQL 的 Buffer Pool,我們也可以在使用 open 打開文件時指定爲 Direct I/O 來繞開 Page Cache,所以說是否使用 Page Cache 還是由應用程序自己決定,Linux 內核只是提供了這種機制,並非要求我們強制使用。
3 Linux 中 Page Cache 含義的變化
在 Linux 的實現中,文件 Cache 分爲兩個層面,一是 Page Cache,另一個是 Buffer Cache(塊緩存)。page cache 用於緩存文件的頁數據,大小通常爲 4K;Buffer cache 用於緩存塊設備(如磁盤)的塊數據,大小通常爲 1K。
在 Linux2.4 版本的內核之前,page cache 和 buffer cache 是完全分離的。但是塊設備大多數是磁盤,磁盤上的數據又大多通過文件系統來組織,這種設計導致很多數據被緩存了兩次,浪費內存空間。
所以在 2.4 版本內核之後,兩塊內存近似融合在了一起,如果一個文件的頁加載到了 page cache,那麼 buffer cache 只需要維護塊指向頁的指針。
在 2.6 版本內核中,page cache 和 buffer cache 進一步結合。每一個 Page Cache 包含若干 Buffer Cache。將文件一頁一頁緩存到 page cache 中,buffer cache 裏面的指針指向磁盤 block。
2.6 內核中的 buffer cache 和 page cache 在處理上是保持一致的,但是存在概念上的差別,page cache 是針對文件的 cache,buffer 是針對磁盤塊數據的 cache,僅此而已。
4 Page Cache 大小的計算
通過命令 cat /proc/meminfo 可以看到 Linux 內存管理統計相關的各項數據:
Page Cache 的大小有如下計算公式:
Page Cache = Buffers + Cached + SwapCached = Active(file) + Inactive(file) + Shmem + SwapCached
先對等號左邊的字段做一個說明:
-
Buffers 是對原始磁盤塊的臨時存儲,也就是用來緩存磁盤的數據,一般不會特別大(20MB 左右),Buffers 既可以用作 “將要寫入磁盤數據的緩存”,也可以用作 “從磁盤讀取數據的緩存”。
-
Cached 是從磁盤讀取文件的內存頁緩存,但是不包括 SwapCached,也就是用來緩存從文件讀取的數據。Cached 既可以用作 “從文件讀取數據的頁緩存”,也可以用作 “寫文件的頁緩存”。
-
SwapCached 是在打開了 Swap 分區後,把 Inactive(anon)+Active(anon) 這兩項裏的匿 名頁給交換到磁盤(swap out),然後再讀入到內存(swap in)後分配的內存。由於讀入到 內存後原來的 Swap File 還在,所以 SwapCached 也可以認爲是 File-backed page,即屬 於 Page Cache。這樣做的目的也是爲了減少 I/O。注意,SwapCached 只在 Swap 分區打開的情況下才會有,我這個環境是關閉掉 swap 的,所以 SwapCached 爲 0。
再來看右邊的字段:
-
在 Page Cache 中,Active(file)+Inactive(file) 是 File-backed page(與文件對應的內存 頁),是最需要關注的部分。因爲我們平時用的 mmap() 內存映射方式和 buffered I/O 來消 耗的內存就屬於這部分。Active 和 Inactive 的區別在於內存空間中是否包含最近被使用過的數據。當物理內存不足,不得不釋放正在使用的內存空間時,會優先釋放 Inactive 的內存空間。Linux 內核中使用 LRU 表來分別記錄對應的這兩類文件內存頁。
-
Page Cache 中的 Shmem 是指匿名共享映射這種方式分配的內存 (free 命令中 shared 這一項),比如 tmpfs(臨時文件系統)。
5 free 命令看到的 buff/cache 又是什麼
Page Cache 的概念很容易跟 free 命令看到的 buff/cache 混淆,所以這裏我們有必要區分一下。
free 命令中的 buff/cache 是由 Buffers、Cached 和 SReclaimable 這三項組成的,它強調的是內存的可回收性,也就是說,可以被回收的內存會 統計在這一項。它只是 free 命令爲了統計內存可回收性人爲將這三個值進行統計到一起的。
buff/cache 的大小還是來源於 / proc/meminfo 中看到的內存統計信息,計算公式如下:
buff/cache = Buffers + Cached + SReclaimable
Buffers 和 Cached 的含義前面已經講過了,我們來看看 SReclaimable:
SReclaimable 是 Slab 的一部分,是可以被回收的,例如緩存,linux 內核使用 Slab 機制,管理文件系統的目錄項和索引節點的緩存。Slab 包括兩部分,其中的可回收部分,是指可以被回收的內核內存,包括目錄項(dentry) 和索引節點( inode )的緩存等,用 SReclaimable 記錄;而不可回收部分,用 SUnreclaim 記錄。
Linux 文件系統爲每個文件都分配兩個數據結構,索引節點(index node)和目錄項(directory entry)。它們主要用來記錄文件的元信息和目錄結構。
-
索引節點,簡稱爲 inode,用來記錄文件的元數據,比如 inode 編號、文件大小、訪問權限、修改日期、數據的位置等。索引節點和文件一一對應,它跟文件內容一樣,都會被持久化存儲到磁盤中。所以記住,索引節點同樣佔用磁盤空間。
-
目錄項,簡稱爲 dentry,用來記錄文件的名字、索引節點指針以及與其他目錄項的關聯關係。多個關聯的目錄項,就構成了文件系統的目錄結構。不過,不同於索引節點,目錄項是由內核維護的一個內存數據結構,所以通常也被叫做目錄項緩存。
換句話說,索引節點是每個文件的唯一標誌,而目錄項維護的正是文件系統的樹狀結構。目錄項和索引節點的關係是多對一,你可以簡單理解爲,一個文件可以有多個別名。舉個例子,通過硬鏈接爲文件創建的別名,就會對應不同的目錄項,不過這些目錄項本質上還是鏈接同一個文件,所以,它們的索引節點相同。
目錄項本身就是一個內存緩存,而索引節點則是存儲在磁盤中的數據。在前面的 Buffers 和 Cached 原理中,我們提到過,爲了協調慢速磁盤與快速內存和 CPU 的性能差異,文件內容會緩存到頁緩存 Cache 中。其實,這些索引節點也會緩存到內存中,加速文件的訪問。
可以通過下面這張圖,理解一下目錄項、索引節點以及文件數據的關係:
6 總結
內存頁包括文件頁和匿名頁,內核緩存的磁盤數據(Buffer)和內核緩存的文件數據(Cache)都叫作文件頁,包括 page cache、slab 中的 dcache、icache、用戶進程的可執行程序的代碼段。
匿名頁包括進程使用各種 api(malloc,mmap,brk/sbrk)申請到的物理內存 (這些 api 通常只是申請虛擬地址,真實的頁分配發生在 page fault 中),包括堆、棧,進程間通信中的共享內存,bss 段,數據段,tmpfs 的頁。
文件頁和匿名頁兩個內存的區別在於,物理內存的內容是否與物理磁盤上的文件相關聯,文件頁與物理磁盤的文件是有關聯的,而匿名頁沒有。
可以看出來,Page Cache 的大小等於內核磁盤數據和文件數據的緩存與匿名頁通過 Swap 機制交換出去的內存大小之和,也等於活躍文件頁、未活躍文件頁、匿名共享映射內存與匿名頁通過 Swap 機制交換出去的內存大小之和。
而 free 命令看到的 buff/cache 等於內核磁盤數據和文件數據的緩存與 Slab 機制中文件系統的目錄項和索引節點的緩存的可回收部分之和,指的是可直接回收的內存大小。
所以也可以看出來,文件頁的緩存,在內存不足時是可以直接回收的(或者是髒頁先會寫到磁盤再回收),而匿名頁是程序動態申請的內存,是不能直接回收的,即使是匿名頁通過 Swap 機制換出的內存,以後也是得再從磁盤換入的。
另外,對於上面 Page Cache 和 buff/cache 的計算公式,我們需要注意一下,在做比較的過程中,一定要考慮到這些數據是動態變化的,而且執行 命令本身也會帶來內存開銷,所以這個等式未必會嚴格相等。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/mbtvKSKDfQcWyyOZCe7sdw