内存中的Buffer和Cache

课程连接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 区别。

你可能感兴趣的:(Linux基础优化,内存,buffer,cache)