天天看點

記憶體中的Buffer和Cache一 free 資料的來源二 proc 檔案系統三 案例四 小結

課程連接配接https://time.geekbang.org/column/intro/100020901

這裡是學習課程的時候記錄的一些學習筆記
free -mh
              total        used        free      shared  buff/cache   available
Mem:           1.8G        291M        745M        8.3M        802M        1.3G
Swap:            0B          0B          0B
           

這個界面包含了實體記憶體 Mem 和交換分區 Swap 的具體使用情況,比如總記憶體、已用記憶體、緩存、可用記憶體等。其中緩存是 Buffer 和 Cache 兩部分的總和 。這裡的大部分名額都比較容易了解,但 Buffer 和 Cache 可能不太好區分。從字面上來說,Buffer 是緩沖區,而 Cache 是緩存,兩者都是資料在記憶體中的臨時存儲。

一 free 資料的來源

從 free 的手冊中,你可以看到 buffer 和 cache 的說明。

  • Buffers 是核心緩沖區用到的記憶體,對應的是 /proc/meminfo 中的 Buffers 值。
  • Cache 是核心頁緩存和 Slab 用到的記憶體,對應的是 /proc/meminfo 中的 Cached 與 SReclaimable 之和。

二 proc 檔案系統

/proc 是 Linux 核心提供的一種特殊檔案系統,是使用者跟核心互動的接口。比方說,使用者可以從 /proc 中查詢核心的運作狀态和配置選項,查詢程序的運作狀态、統計資料等,當然,你也可以通過 /proc 來修改核心的配置。

proc 檔案系統同時也是很多性能工具的最終資料來源。比如我們剛才看到的 free ,就是通過讀取/proc/meminfo,得到記憶體的使用情況。

執行 man proc,檢視 proc 檔案系統的詳細文檔。

通過這個文檔,我們可以看到:

  • Buffers 是對原始磁盤塊的臨時存儲,也就是用來緩存磁盤的資料,通常不會特别大(20MB 左右)。這樣,核心就可以把分散的寫集中起來,統一優化磁盤的寫入,比如可以把多次小的寫合并成單次大的寫等等。
  • Cached 是從磁盤讀取檔案的頁緩存,也就是用來緩存從檔案讀取的資料。這樣,下次通路這些檔案資料時,就可以直接從記憶體中快速擷取,而不需要再次通路緩慢的磁盤。SReclaimable 是 Slab 的一部分。
  • Slab 包括兩部分,其中的可回收部分,用 SReclaimable 記錄;而不可回收部分,用 SUnreclaim 記錄

三 案例

1. 準備工作

# 清理檔案頁、目錄項、Inodes等各種緩存
$ echo 3 > /proc/sys/vm/drop_caches
           

這裡的 /proc/sys/vm/drop_caches ,就是通過 proc 檔案系統修改核心行為的一個示例,寫入 3 表示清理檔案頁、目錄項、Inodes 等各種緩存。

2.場景 1:磁盤和檔案寫案例

vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 3  0      0 1444884    396 139636    0    0    65    27  231  540  0  0 99  0  0
 0  0      0 1444884    396 139628    0    0     0     0  339  690  1  0 99  0  0
 0  0      0 1444884    396 139628    0    0     0     0  291  633  0  0 100  0  0
 0  0      0 1436836   4232 142080    0    0  6288     0 1017 1265  4  3 78 15  0
 0  0      0 1436836   4232 142084    0    0     0     0  290  633  0  1 99  0  0
           

輸出界面裡, 記憶體部分的 buff 和 cache ,以及 io 部分的 bi 和 bo 就是我們要關注的重點。

buff 和 cache 就是我們前面看到的 Buffers 和 Cache,機關是 KB。

bi 和 bo 則分别表示塊裝置讀取和寫入的大小,機關為塊 / 秒。因為 Linux 中塊的大小是 1KB,是以這個機關也就等價于 KB/s。

正常情況下,空閑系統中,你應該看到的是,這幾個值在多次結果中一直保持不變。接下來,到第二個終端執行 dd 指令,通過讀取随機裝置,生成一個 500MB 大小的檔案:

dd if=/dev/urandom of=/tmp/file bs=1M count=500
           

然後再回到第一個終端,觀察 Buffer 和 Cache 的變化情況:

0  0      0 1436836   4240 142080    0    0     0    28  296  645  0  0 100  0  0
 0  0      0 1436836   4240 142084    0    0     0     0  274  607  0  0 100  0  0
 0  0      0 1436836   4240 142084    0    0     0     0  270  599  1  0 99  0  0
 1  0      0 1436836   4240 142084    0    0     0     0  264  594  0  1 99  0  0
 0  0      0 1436836   4240 142084    0    0     0     0  271  608  0  0 100  0  0
 0  0      0 1436836   4240 142084    0    0     0     0  274  599  1  0 99  0  0
 0  0      0 1436836   4240 142084    0    0     0     4  267  596  0  0 100  0  0
 0  0      0 1436836   4240 142084    0    0     0     0  267  598  0  0 100  0  0
 0  0      0 1436836   4240 142084    0    0     0     0  272  607  1  0 99  0  0
 4  0      0 1433224   4524 144168    0    0   360     0  572  605  0 25 75  0  0
 5  0      0 1421720   4524 155728    0    0     0     0 1283  427  0 100  0  0  0
 2  0      0 1411192   4536 166256    0    0     4 19532 1247  433  0 98  0  2  0
 1  0      0 1400556   4536 176740    0    0     0     0 1258  414  0 100  0  0  0
 1  0      0 1390044   4536 187244    0    0     0     0 1246  413  0 100  0  0  0
 2  0      0 1378540   4536 198812    0    0     0     0 1233  403  0 100  0  0  0
 2  0      0 1368152   4536 209300    0    0     0     0 1228  413  0 100  0  0  0
 1  0      0 1357640   4544 219792    0    0     0    20 1223  424  0 100  0  0  0
 1  0      0 1346136   4544 231372    0    0     0     0 1205  404  0 100  0  0  0
 1  0      0 1335500   4544 241872    0    0     0     0 1201  404  0 100  0  0  0
 4  0      0 1324988   4544 252360    0    0     0     0 1247  409  0 100  0  0  0
 2  0      0 1313520   4544 263928    0    0     0     0 1223  411  0 100  0  0  0
10  0      0 1302968   4552 274424    0    0     0    12 1231  416  0 100  0  0  0
 2  0      0 1292428   4552 284936    0    0     0     0 1200  411  0 100  0  0  0
 3  0      0 1282012   4552 295436    0    0     0     0 1226  409  0 100  0  0  0
 3  0      0 1270356   4552 307008    0    0     0     0 1221  420  0 100  0  0  0
 1  0      0 1259812   4552 317492    0    0     0 53272 1223  410  0 100  0  0  0
 2  0      0 1249300   4560 328000    0    0     0    20 1203  423  0 100  0  0  0
 2  0      0 1238912   4560 338496    0    0     0     0 1203  430  0 100  0  0  0
 2  0      0 1227408   4560 350024    0    0     0     0 1197  399  0 100  0  0  0
 1  0      0 1216908   4560 360600    0    0     0     0 1192  410  0 100  0  0  0
 3  0      0 1206220   4564 371264    0    0     4 148480 1231  420  0 100  0  0  0
           

通過觀察 vmstat 的輸出,我們發現,在 dd 指令運作時, Cache 在不停地增長,而 Buffer 基本保持不變。再進一步觀察 I/O 的情況,你會看到,在 Cache 剛開始增長時,塊裝置 I/O 很少,bi 隻出現了一次 488 KB/s,bo 則隻有一次 4KB。而過一段時間後,才會出現大量的塊裝置寫,比如 bo 變成了 122880。當 dd 指令結束後,Cache 不再增長,但塊裝置寫還會持續一段時間,并且,多次 I/O 寫的結果加起來,才是 dd 要寫的 500M 的資料。

下面的指令對環境要求很高,需要你的系統配置多塊磁盤,并且磁盤分區 /dev/sdb1 還要處于未使用狀态。如果你隻有一塊磁盤,千萬不要嘗試,否則将會對你的磁盤分區造成損壞。如果你的系統符合标準,就可以繼續在第二個終端中,運作下面的指令。清理緩存後,向磁盤分區 /dev/sdb1 寫入 2GB 的随機資料:

# 首先清理緩存
$ echo 3 > /proc/sys/vm/drop_caches
# 然後運作dd指令向磁盤分區/dev/sdb1寫入2G資料
$ dd if=/dev/urandom of=/dev/sdb1 bs=1M count=2048
           

然後回到終端1 檢視io狀态

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 7584780 153592  97436    0    0   684     0   31  423  1 48 50  2  0
 1  0      0 7418580 315384 101668    0    0     0     0   32  144  0 50 50  0  0
 1  0      0 7253664 475844 106208    0    0     0     0   20  137  0 50 50  0  0
 1  0      0 7093352 631800 110520    0    0     0     0   23  223  0 50 50  0  0
 1  1      0 6930056 790520 114980    0    0     0 12804   23  168  0 50 42  9  0
 1  0      0 6757204 949240 119396    0    0     0 183804   24  191  0 53 26 21  0
 1  1      0 6591516 1107960 123840    0    0     0 77316   22  232  0 52 16 33  0
           

從這裡你會看到,雖然同是寫資料,寫磁盤跟寫檔案的現象還是不同的。

寫磁盤時(也就是 bo 大于 0 時),Buffer 和 Cache 都在增長,但顯然 Buffer 的增長快得多。這說明,寫磁盤用到了大量的 Buffer,這跟我們在文檔中查到的定義是一樣的。

對比兩個案例,我們發現,寫檔案時會用到 Cache 緩存資料,而寫磁盤則會用到 Buffer 來緩存資料。是以,回到剛剛的問題,雖然文檔上隻提到,Cache 是檔案讀的緩存,但實際上,Cache 也會緩存寫檔案時的資料。

3.場景 2:磁盤和檔案讀案例

我們回到第二個終端,運作下面的指令。清理緩存後,從檔案 /tmp/file 中,讀取資料寫入空裝置:

# 首先清理緩存
$ echo 3 > /proc/sys/vm/drop_caches
# 運作dd指令讀取檔案資料
$ dd if=/tmp/file of=/dev/null
           

終端一檢視狀态

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  1      0 7724164   2380 110844    0    0 16576     0   62  360  2  2 76 21  0
 0  1      0 7691544   2380 143472    0    0 32640     0   46  439  1  3 50 46  0
 0  1      0 7658736   2380 176204    0    0 32640     0   54  407  1  4 50 46  0
 0  1      0 7626052   2380 208908    0    0 32640    40   44  422  2  2 50 46  0
           

觀察 vmstat 的輸出,你會發現讀取檔案時(也就是 bi 大于 0 時),Buffer 保持不變,而 Cache 則在不停增長。這跟我們查到的定義“Cache 是對檔案讀的頁緩存”是一緻的。

首先,回到第二個終端,運作下面的指令。清理緩存後,從磁盤分區 /dev/sda1 中讀取資料,寫入空裝置:

# 首先清理緩存
$ echo 3 > /proc/sys/vm/drop_caches
# 運作dd指令讀取檔案
$ dd if=/dev/sda1 of=/dev/null bs=1M count=1024
           

然後,再回到終端一,觀察記憶體和 I/O 的變化情況:

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 7225880   2716 608184    0    0     0     0   48  159  0  0 100  0  0
 0  1      0 7199420  28644 608228    0    0 25928     0   60  252  0  1 65 35  0
 0  1      0 7167092  60900 608312    0    0 32256     0   54  269  0  1 50 49  0
 0  1      0 7134416  93572 608376    0    0 32672     0   53  253  0  0 51 49  0
 0  1      0 7101484 126320 608480    0    0 32748     0   80  414  0  1 50 49  0
           

觀察 vmstat 的輸出,你會發現讀磁盤時(也就是 bi 大于 0 時),Buffer 和 Cache 都在增長,但顯然 Buffer 的增長快很多。這說明讀磁盤時,資料緩存到了 Buffer 中。

  • Buffer 既可以用作“将要寫入磁盤資料的緩存”,也可以用作“從磁盤讀取資料的緩存”。
  • Cache 既可以用作“從檔案讀取資料的頁緩存”,也可以用作“寫檔案的頁緩存”。

簡單來說,Buffer 是對磁盤資料的緩存,而 Cache 是檔案資料的緩存,它們既會用在讀請求中,也會用在寫請求中。

四 小結

Buffer 和 Cache 分别緩存磁盤和檔案系統的讀寫資料。

  • 從寫的角度來說,不僅可以優化磁盤和檔案的寫入,對應用程式也有好處,應用程式可以在資料真正落盤前,就傳回去做其他工作。
  • 從讀的角度來說,既可以加速讀取那些需要頻繁通路的資料,也降低了頻繁 I/O 對磁盤的壓力。

磁盤是一個塊裝置,可以劃分為不同的分區;在分區之上再建立檔案系統,挂載到某個目錄,之後才可以在這個目錄中讀寫檔案。

其實 Linux 中“一切皆檔案”,而文章中提到的“檔案”是普通檔案,磁盤是塊裝置檔案,這些大家可以執行 “ls -l <路徑>”

檢視它們的差別(輸出的含義如果不懂請 man ls 查詢)。

在讀寫普通檔案時,會經過檔案系統,由檔案系統負責與磁盤互動;而讀寫磁盤或者分區時,就會跳過檔案系統,也就是所謂的“裸I/O“。這兩種讀寫方式所使用的緩存是不同的,也就是文中所講的

Cache 和 Buffer 差別。

繼續閱讀