什麼是 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

先對等號左邊的字段做一個說明:

再來看右邊的字段:

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)。它們主要用來記錄文件的元信息和目錄結構。

換句話說,索引節點是每個文件的唯一標誌,而目錄項維護的正是文件系統的樹狀結構。目錄項和索引節點的關係是多對一,你可以簡單理解爲,一個文件可以有多個別名。舉個例子,通過硬鏈接爲文件創建的別名,就會對應不同的目錄項,不過這些目錄項本質上還是鏈接同一個文件,所以,它們的索引節點相同。

目錄項本身就是一個內存緩存,而索引節點則是存儲在磁盤中的數據。在前面的 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