應用程序產生Page Cache的邏輯示意圖
紅色的地方就是 Page Cache,很明顯,Page Cache是內核管理的內存,也就是說,它屬于內核不屬于用戶。
如何觀察 Page Cache? 在 linux 上直接查看 Page Cache 的方式有很多,包括 /proc/meminfo、free 、/proc/vmstat 命令等,它們的內容其實是一致的。拿 /proc/meminfo 命令舉例看一下:
$ cat /proc/meminfo
...
Buffers: 1224 kB
Cached: 111472 kB
SwapCached: 36364 kB
Active: 6224232 kB
Inactive: 979432 kB
Active(anon): 6173036 kB
Inactive(anon): 927932 kB
Active(file): 51196 kB
Inactive(file): 51500 kB
...
Shmem: 10000 kB
...
SReclaimable: 43532 kB
根據上面的數據,你可以簡單得出這樣的公式(等式兩邊之和都是 112696 KB):
Buffers + Cached + SwapCached = Active(file) + Inactive(file) + Shmem + SwapCached
那么等式兩邊的內容就是我們平時說的 Page Cache。請注意你沒有看錯,兩邊都有Swap Cached,之所以要把它放在等式里,就是說它也是 Page Cache 的一部分。等式右邊這些項把 Buffers 和 Cached 做了一下細分,分為了 Active(file),Inactive(file) 和 Shmem,因為 Buffers 更加依賴于內核實現,在不同內核版本中它的含義可能有些不一致,而等式右邊和應用程序的關系更加直接,所以我們從等式右邊來分析.
在 Page Cache 中,Active(file)+Inactive(file) 是 File-backed page(與文件對應的內存頁),是你最需要關注的部分。因為你平時用的 mmap() 內存映射方式和 buffered I/O來消耗的內存就屬于這部分,最重要的是,這部分在真實的生產環境上也最容易產生問題,我們在接下來的課程案例篇會重點分析它。
而 SwapCached 是在打開了 Swap 分區后,把 Inactive(anon)+Active(anon) 這兩項里的匿名頁給交換到磁盤(swap out),然后再讀入到內存(swap in)后分配的內存。由于讀入到內存后原來的 Swap File 還在,所以 SwapCached 也可以認為是 File-backed page,即屬于 Page Cache。這樣做的目的也是為了減少 I/O。
通過下面簡單的示意圖明白 SwapCached 是怎么產生的:
SwapCached 只在 Swap 分區打開的情況下才會有,而我建議你在生產環境中關閉 Swap 分區,因為 Swap 過程產生的 I/O 會很容易引起性能抖動。

Page Cache 中的 Shmem 是指匿名共享映射這種方式分配的內存(free 命令中 shared 這一項),比如 tmpfs(臨時文件系統),這部分在真實的生產環境中產生的問題比較少。
free 命令中的 buff/cache 究竟是指什么呢?
free 命令也是通過解析 /proc/meminfo 得出這些統計數據的,這些都可以通過 free 工具的源碼來找到。free 命令的源碼是開源,你可以去看下 procfs里的 free.c 文件,源碼是最直接的理解方式,它會加深你對 free 命令的理解。
$ free -k
total used free shared buff/cache availabl
Mem: 7926580 7277960 492392 10000 156228 43068
Swap: 8224764 380748 7844016
通過 procfs 源碼里面的proc/sysinfo.c這個文件,你可以發現 buff/cache 包括下面這幾項:
buff/cache = Buffers + Cached + SReclaimable
通過前面的數據我們也可以驗證這個公式: 1224 + 111472 + 43532 的和是 156228。
free 命令中的 buff/cache 是由 Buffers、Cached 和 SReclaimable 這三項組成的,它強調的是內存的可回收性,也就是說,可以被回收的內存會統計在這一項。
SReclaimable 是指可以被回收的內核內存,包括 dentry 和 inode 等。
掌握了 Page Cache 具體由哪些部分構成之后,在它引發一些問題時,你就能夠知道需要去觀察什么。比如說,應用本身消耗內存(RSS)不多的情況下,整個系統的內存使用率還是很高,那不妨去排查下是不是 Shmem(共享內存) 消耗了太多內存導致的。
為什么需要 Page Cache?
第一張圖你其實已經可以直觀地看到,標準 I/O 和內存映射會先把數據寫入到 Page Cache,這樣做會通過減少 I/O 次數來提升讀寫效率。
看一個具體的例子。首先,我們來生成一個 1G 大小的新文件,然后把 Page Cache 清空,確保文件內容不在內存中,以此來比較第一次讀文件和第二次讀文件耗時的差異。具體的流程如下。
- 先生成一個 1G 的文件:
dd if = /dev/zero of = /home/test/dd.outbs = 4096 count = ((1024*256))
2 . 清空 Page Cache,需要先執行一下 sync 來將臟頁同步到磁盤再去 drop cache。
sync && echo 3 > /proc/sys/vm/drop_caches
3 . 第一次讀取文件的耗時如下:
$ time cat /home/test/dd.out &> /dev/null
real 0m5.733s
user 0m0.003s
sys 0m0.213s
再次讀取文件的耗時如下:
$ time cat /home/test/dd.out &> /dev/null
real 0m0.132s
user 0m0.001s
sys 0m0.130s
第二次讀取文件的耗時遠小于第一次的耗時,這是因為第一次是從磁盤來讀取的內容,磁盤 I/O 是比較耗時的,而第二次讀取的時候由于文件內容已經在第一次讀取時被讀到內存了,所以是直接從內存讀取的數據,內存相比磁盤速度是快很多的。這就是 Page Cache 存在的意義:減少 I/O,提升應用的 I/O 速度。