課程連接配接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 差別。