Linux 面試經典:buff 和 cache 到底差在哪?從 vmstat 看懂 Page Cache 與 Buffer

Linux 面試經典:buff 和 cache 到底差在哪?從 vmstat 看懂 Page Cache 與 Buffer

buffcache 都是拿記憶體來暫存資料,那它們到底差在哪?「我的 free 記憶體怎麼只剩一點點,是不是系統快掛了?」

這篇文章簡述 Linux 記憶體的機制,它們各自快取什麼、為什麼現代 kernel 上 buff 通常小得可憐,最後用一個 vmstat 的小實驗,實際讓 buffcache 分別「動起來」。

Buffer 與 Cache

教科書式的定義:

  • Buffer(緩衝區):暫存「正在搬運中」的資料。當資料在兩個速度不對等的裝置之間傳輸(例如應用程式寫入硬碟),資料不會直接落到目的地,而是先進入 buffer,讓 kernel 可以把零碎的寫入合併成較大的批次,一次性刷到磁碟上。這對循序 I/O 特別有效。
  • Cache(快取):暫存「已經讀過、之後可能再用到」的資料。把常用的資料留一份在比較快的儲存層(RAM),下次要用時就不必再去慢速的磁碟撈一次。CPU cache、disk cache、filesystem cache 都是同一個精神。

一句話總結:buffer 著重協調資料傳輸,cache 著重保留資料以便重用 1。不過這只是通用概念,不能直接解讀成 Linux 的「buffer 只管寫、cache 只管讀」:一般檔案的 buffered read、buffered write 和 mmap() 都會經過 Page Cache;寫入的頁面會先成為 dirty page,再由 kernel 寫回儲存裝置 2 3

在 Linux 上的實際意義:Page Cache vs Buffer Cache

上面的定義是通用概念,但落到 Linux 記憶體管理的 BuffersCached(也就是 /proc/meminfo 裡的兩個欄位),意義更具體 4

欄位 快取的對象 典型場景
Cached(Page Cache) 一般檔案的內容,也包含 tmpfs / shmem;不包含 SwapCached catgrep、一般檔案的 buffered read / write
Buffers(Buffer Cache) block device address space 裡的 raw disk blocks,以及部分檔案系統使用 buffer head 管理的磁碟上 metadata block /dev/sdX 做 buffered I/O、部分檔案系統的 journal / metadata I/O

💡 記憶體統計有兩個容易忽略的細節

Cached 不全是磁碟快取。 tmpfs / shmem 也包含在 Cached 裡;若只是想粗估 disk-backed file cache,可以看 Cached - Shmem。在沒有 swap、且擁有者也沒有釋放頁面的情況下,這部分不能像 clean file cache 一樣直接丟棄 5

不是所有 filesystem metadata 都算進 Buffers 只有部分檔案系統的磁碟上 metadata blocks 會使用 buffer head。VFS 的 inode object 與 dentry object 是獨立的記憶體內快取,通常計入 Slab / SReclaimable 6 4

這裡有一個歷史脈絡值得知道:早期的 Page Cache 和 Buffer Cache 是兩套獨立快取;兩者後來合併,並在 Linux 2.4 時代移除檔案資料寫回所需的額外複本。現代 kernel 的 buffer cache 仍然存在,但 buffer head 會指向 Page Cache 裡的頁面 7 8。因此,Buffers 現在主要反映 raw disk blocks,而不是一般檔案內容;在多數工作負載中,它通常遠小於 Cached

如果你今天在面試或除錯時被問到這題,更好的回答是:

Cached 主要是 Page Cache(也包含 tmpfs / shmem),Buffers 是 block device 的 raw disk blocks,以及部分檔案系統使用的 metadata blocks;inode / dentry object 則多半在 reclaimable slab。procps-ng 3.3.10 起,free 預設用 buff/cache 彙總顯示,並加入 available;目前版本的 buff/cache 實際是 Buffers + Cached + SReclaimable 9 10

從 I/O 堆疊看 Page Cache 與 Buffer Cache 的位置

把整個 Linux I/O 路徑攤開來看,兩者的分工會更直觀 11

Linux I/O 路徑:Page Cache 與 Buffer Cache 的位置

Linux I/O 路徑:Page Cache 與 Buffer Cache 的位置

圖中的 filesystem metadata 指磁碟上的 metadata blocks;VFS 的 inode / dentry objects 則是前面提到的 slab caches。

  • 走 filesystem 的路:應用程式 read() / write() 一般檔案 → VFS → Page Cache。讀取命中(cache hit)就直接從 RAM 回傳;miss 或寫回 dirty page 時,才透過 filesystem 走到 Block Layer 與磁碟 2
  • 走 raw block device 的路:對 /dev/sdX 這類 block device 做一般 buffered I/O 時,不經過 mounted filesystem 的檔案對應邏輯,但仍會使用該 block device 自己的 page-cache mapping;這些頁面計入 Buffers 12
  • 部分檔案系統也會用 buffer head 管理 journal 或磁碟上的 metadata blocks,但 VFS 的 inode / dentry object 是另一套 slab cache
  • 兩條路最後都會匯入 Block Layer → Device Driver → 實體裝置。資料庫這類自己管理快取的應用,則可以用 O_DIRECT 繞過 Page Cache;它仍會經過 filesystem 與 block layer,而不是直接呼叫硬體 2

用 vmstat 觀察 buff 與 cache

vmstat 是觀察系統記憶體、I/O 與 CPU 的基本工具。vmstat 1 5 表示每 1 秒取樣一次、共 5 次。第一列資料是自開機以來的平均值,後續各列才是每個取樣區間的數值 13

$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0      0 123456  7890  56789    0    0   100   200  300  400 10  5 80  5  0
 0  1      0 120000  7895  56800    0    0   150   250  320  410 12  4 78  6  0
 2  0      0 118500  7898  56810    0    0   180   220  330  420 9   6 82  3  0
 1  0      0 116800  7902  56825    0    0   120   210  310  430 11  3 81  5  0
 0  0      0 115000  7905  56835    0    0   140   230  300  390 13  4 77  6  0

跟這篇主題相關的欄位:

  • free:閒置記憶體
  • buff:作為 buffer 使用的記憶體(對應 /proc/meminfoBuffers
  • cache:procps-ng 所彙整的 cache(目前為 Cached + SReclaimable
  • bi / bo:每秒從 block device 讀入/寫出的 KiB;不受 vmstat -S 的顯示單位選項影響 13 14

上面的數字是示意資料。實際觀察時,若 bi / bo 持續有量,而 free 下降、buffcache 上升,通常代表 kernel 正在把 block 或檔案資料留在記憶體裡;單看這個現象並不表示記憶體異常 15

實驗:讓 buff 和 cache 分別動起來

光看定義還是抽象,直接做實驗最清楚。先清空快取,建立乾淨的基準:

# 把 dirty page 刷回磁碟,並清空 page cache、dentry、inode cache
$ sync
$ echo 3 | sudo tee /proc/sys/vm/drop_caches

drop_caches=3 會丟棄 clean page cache 與可回收的 slab objects(包括 dentry / inode cache);它不會丟掉 dirty objects,所以先執行 sync 能讓實驗結果更乾淨。這個介面適合測試與除錯,不建議當成日常「釋放記憶體」的手段,因為後續重建快取會產生額外 I/O 與 CPU 成本 16

情境一:讀一般檔案 → cache 上升

先確認測試檔位於 disk-backed filesystem,而不是 tmpfs。開一個終端跑 vmstat -y 1-y 省略自開機以來的第一列),另一個終端讀一個大檔案:

$ dd if=/var/tmp/bigfile of=/dev/null bs=1M status=progress
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 7452100  2120   98500    0    0     0     0  120  210  1  0 99  0  0
 1  0      0 6890200  2135  652800    0    0 524288    0  850 1600  2 12 70 16  0
 1  0      0 6320400  2150 1214500    0    0 524288    0  860 1620  1 13 68 18  0

檔案內容進的是 Page Cache,所以這組示意輸出裡 cache 每秒增加約 500 MiB,buff 幾乎不動。實際增幅會受檔案大小、讀取速度、既有快取與其他系統活動影響。

情境二:直接讀 raw block device → buff 上升

先重新執行前面的 syncdrop_caches,再用 lsblk 確認裝置名稱,並把下列 /dev/sdX 換成實際裝置。這個命令只讀取裝置並丟到 /dev/null;不要把 if / of 寫反,也不要加上 iflag=direct,否則會繞過想觀察的 cache:

$ sudo dd if=/dev/sdX of=/dev/null bs=1M count=1024 status=progress
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 7450300   2150  99200    0    0     0     0  115  200  1  0 99  0  0
 1  0      0 6980100 471300  99800    0    0 460800    0  830 1550  1 11 71 17  0
 1  0      0 6420500 1023800 100300   0    0 524288    0  855 1590  2 12 69 17  0

這次對 block device 做 buffered read,資料進入該裝置的 page-cache mapping,而 /proc/meminfo 把這些頁面列在 Buffers,所以換成 buff 明顯上升、cache 幾乎不動 12

兩個實驗對照,buffcache 的分工一目了然:一般檔案內容主要反映在 cache,block device mapping 裡的 raw blocks 反映在 buff。實驗數值不必與範例完全相同;背景服務、檔案系統和 kernel 版本都可能使其他欄位同時變動。

常見誤解:free 很低不是壞事

最後回頭解決那個常見的焦慮。Linux 會利用閒置 RAM 做快取,所以 free 很低本身不是故障。當應用程式需要記憶體時,clean file cache 與一部分 reclaimable slab 可以被回收;dirty page 要先寫回,正在使用的 slab 或沒有 swap 可退的 shmem 則不一定能立即回收 4 10

判斷記憶體是否吃緊,別只看 free

  • free 指令輸出的 available 欄位:它估算在不進行 swap 的情況下,還有多少記憶體可供新應用程式使用;計算時會考慮 page cache、reclaimable slab 與 zone low watermarks 4
  • vmstatsi / so:持續的 swap in / out 是記憶體壓力的重要訊號;只看到 swap 已使用一些空間,不代表系統此刻正在 thrashing 17

總結

  • 一般概念上,buffer 著重協調資料傳輸,cache 著重保留可重用資料;這不等於 Linux 的「寫 vs 讀」
  • Linux 的 Cached 主要反映 Page Cache,也包含 tmpfs / shmem;一般檔案的 buffered read 與 write 都會經過它
  • Buffers 主要是 block device mapping 的 raw disk blocks,以及部分檔案系統使用的 metadata blocks;inode / dentry object 通常在 reclaimable slab
  • Page Cache 與 Buffer Cache 在 Linux 2.4 時代完成統一,現代系統的主要檔案快取是 Page Cache
  • vmstat 1 搭配「讀檔案 vs 讀 raw device」的實驗,可以清楚看到 cachebuff 分別上升
  • free 很低不等於記憶體不足,需進一步確認 available 與 swap 活動

參考資料

Eason Cao
Eason Cao Eason is an engineer working at FANNG and living in Europe. He was accredited as AWS Professional Solution Architect, AWS Professional DevOps Engineer and CNCF Certified Kubernetes Administrator. He started his Kubernetes journey in 2017 and enjoys solving real-world business problems.
comments powered by Disqus