Buffer 是缓冲区,而 Cache 是缓存,两者都是数据在内存中的临时存储。
  避免跟文中的“缓存”一词混淆,而文中的“缓存”,则通指内存中的临时存储。

free

$ man free
       buffers
              Memory used by kernel buffers (Buffers in /proc/meminfo)

       cache
              Memory used by the page cache and slabs (Cached and SReclaimable in /proc/meminfo)

       buff/cache
              Sum of buffers and cache

  Buffers 是内核缓冲区用到的内存,对应的是 /proc/meminfo 中的 Buffers 值。
  Cache 是内核页缓存和 Slab 用到的内存,对应的是 /proc/meminfo 中的 Cached 与 SReclaimable 之和。

proc 文件系统

  /proc 是 Linux 内核提供的一种特殊文件系统,是用户跟内核交互的接口。比方说,用户可以从 /proc 中查询内核的运行状态和配置选项,查询进程的运行状态、统计数据等,当然,你也可以通过 /proc 来修改内核的配置。

$ man proc
/meminfo
	Buffers %lu
	    Relatively temporary storage for raw disk blocks that shouldn't get tremendously large 
	    (20MB or so).
	
	Cached %lu
	   In-memory cache for files read from the disk (the page cache).  Doesn't include SwapCached.
	...
	SReclaimable %lu (since Linux 2.6.19)
	    Part of Slab, that might be reclaimed, such as caches.
	    
	SUnreclaim %lu (since Linux 2.6.19)
	    Part of Slab, that cannot be reclaimed on memory pressure.

  Buffers 是对原始磁盘块的临时存储,也就是用来缓存磁盘的数据,通常不会特别大(20MB 左右)。这样,内核就可以把分散的写集中起来,统一优化磁盘的写入,比如可以把多次小的写合并成单次大的写等等。
  Cached 是从磁盘读取文件的页缓存,也就是用来缓存从文件读取的数据。这样,下次访问这些文件数据时,就可以直接从内存中快速获取,而不需要再次访问缓慢的磁盘。
  SReclaimable 是 Slab 的一部分。Slab 包括两部分,其中的可回收部分,用 SReclaimable 记录;而不可回收部分,用 SUnreclaim 记录。

问题

(1)Buffer 的文档没有提到这是磁盘读数据还是写数据的缓存,而在很多网络搜索的结果中都会提到 Buffer 只是对将要写入磁盘数据的缓存。那反过来说,它会不会也缓存从磁盘中读取的数据呢?
(2)Cache 是对从文件读取数据的缓存,那么它是不是也会缓存写文件的数据呢?

案例

预先安装 apt install sysstat,通过 vmstat 来观察 Buffer 和 Cache 的变化情况。
使用 dd 来模拟磁盘和文件的 I/O,需要观测 I/O 的变化情况。

场景 1:

# 第一个 terminal
$ echo 3 > /proc/sys/vm/drop_caches # 清理文件页、目录项、Inodes 等各种缓存
$ vmstat 1 # 每隔 1 秒输出 1 组数据
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 7743608   1112  92168    0    0     0     0   52  152  0  1 100  0  0
 0  0      0 7743608   1112  92168    0    0     0     0   36   92  0  0 100  0  0

  buff 和 cache 就是我们前面看到的 Buffers 和 Cache,单位是 KB。
  bi 和 bo 则分别表示块设备读取和写入的大小,单位为块 / 秒。因为 Linux 中块的大小是 1KB,所以这个单位也就等价于 KB/s。
  在系统空闲时,这几个值在多次结果中一直保持不变。

# 第二个 terminal,通过读取随机设备,生成一个 500MB 大小的文件
$ dd if=/dev/urandom of=/tmp/file bs=1M count=500

  回到第一个 terminal,在 dd 命令运行时, Cache 在不停地增长,而 Buffer 基本保持不变。
  但在 Cache 刚开始增长时,块设备 I/O 很少,bi 只出现了一次 488 KB/s,bo 则只有一次 4KB。而过一段时间后,才会出现大量的块设备写,比如 bo 变成了 122880。
  当 dd 命令结束后,Cache 不再增长,但块设备写还会持续一段时间,并且,多次 I/O 写的结果加起来,才是 dd 要写的 500M 的数据。

# 第一个 terminal
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 7499460   1344 230484    0    0     0     0   29  145  0  0 100  0  0
 1  0      0 7338088   1752 390512    0    0   488     0   39  558  0 47 53  0  0
 1  0      0 7158872   1752 568800    0    0     0     4   30  376  1 50 49  0  0
 1  0      0 6980308   1752 747860    0    0     0     0   24  360  0 50 50  0  0
 0  0      0 6977448   1752 752072    0    0     0     0   29  138  0  0 100  0  0
 0  0      0 6977440   1760 752080    0    0     0   152   42  212  0  1 99  1  0
...
 0  1      0 6977216   1768 752104    0    0     4 122880   33  234  0  1 51 49  0
 0  1      0 6977440   1768 752108    0    0     0 10240   38  196  0  0 50 50  0

  文档上说 Cache 是文件读的页缓存,怎么现在写文件也有它的份?
  我们再看一个磁盘写的案例:

# 第二个 terminal
$ echo 3 > /proc/sys/vm/drop_caches
# 然后运行 dd 命令向磁盘分区 /dev/sdb1 写入 2G 数据
$ dd if=/dev/urandom of=/dev/sdb1 bs=1M count=2048

  再回到第一个 terminal,观察内存和 I/O 的变化情况:写磁盘时(也就是 bo 大于 0 时),Buffer 和 Cache 都在增长,但显然 Buffer 的增长快得多。说明写文件时会用到 Cache 缓存数据,而写磁盘则会用到 Buffer 来缓存数据。
  所以,Cache 是文件读的缓存,但实际上,Cache 也会缓存写文件时的数据。

# 第一个 terminal
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

场景 2:

# 第二个 terminal
$ echo 3 > /proc/sys/vm/drop_caches
# 从文件 /tmp/file 中,读取数据写入空设备
$ dd if=/tmp/file of=/dev/null

  回到第一个 terminal,观察内存和 I/O 的变化情况:观察 vmstat 的输出,你会发现读取文件时(也就是 bi 大于 0 时),Buffer 保持不变,而 Cache 则在不停增长。
  所以,证明了 Cache 是对文件读的页缓存。

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

  那么,磁盘读又是什么情况呢?

$ echo 3 > /proc/sys/vm/drop_caches
# 从磁盘分区 /dev/sda1 中读取数据,写入空设备
$ dd if=/dev/sda1 of=/dev/null bs=1M count=1024

  回到第一个 terminal,观察内存和 I/O 的变化情况:发现读磁盘时(也就是 bi 大于 0 时),Buffer 和 Cache 都在增长,但显然 Buffer 的增长快很多。这说明读磁盘时,数据缓存到了 Buffer 中。

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

  结论:读文件时数据会缓存到 Cache 中,而读磁盘时数据会缓存到 Buffer 中。
  Buffer 既可以用作“将要写入磁盘数据的缓存”,也可以用作“从磁盘读取数据的缓存”。
  Cache 既可以用作“从文件读取数据的页缓存”,也可以用作“写文件的页缓存”。

  简单来说,Buffer 是对磁盘数据的缓存,而 Cache 是文件内容数据的缓存,它们既会用在读请求中,也会用在写请求中

小结

  Buffer 和 Cache 分别缓存磁盘和文件系统的读写数据。
  从写的角度来说,不仅可以优化磁盘和文件的写入,对应用程序也有好处,应用程序可以在数据真正落盘前,就返回去做其他工作。
  从读的角度来说,既可以加速读取那些需要频繁访问的数据,也降低了频繁 I/O 对磁盘的压力。

实际案例
  磁盘是块设备,可以划分为不同的分区,在分区之上再创建文件系统,挂载到某个目录,之后才可以在这个目录中读写文件。
  (1)Buffer 与特定的 block device 块设备相关联,并且包括文件系统 metadata 元数据的缓存以及跟踪正在进行中的 pages。Cache 仅包含文件数据。 也就是说,Buffer 会记住目录,文件权限,以及跟踪特定块设备正在写入或读取的内存,文件系统的 metadata 是磁盘数据,不是文件数据。 Cache 仅包含文件本身的内容。
  (2)读写普通文件时,会进过经过文件系统,有文件系统负责与磁盘交互;在读写磁盘或者分区时,就会跳过文件系统,也就是所谓的“裸 I/O”。理论上,读文件首先磁盘到Buffer,再由文件系统操作到Cache,读取需要两次,这方式简单但低效,后期Linux将这两步合并成只有一个Cache了。
  (3)数据库使用裸设备是明显的磁盘读写;如果数据库的数据文件在文件系统上就是文件读写。
  (4)socket buffer 是 Cache;VM虚拟机会越过文件系统,直接操作disk,也就是direct I/O。

思考

  如何统计出所有进程的物理内存使用量呢?
  提示:要避免重复计算多个进程同时占用的内存,像是页缓存、共享内存这类。如果你把 ps、top 得到的数据直接相加,就会出现重复计算的问题。这里,我推荐从 /proc/ /smaps 入手。


版权声明:本文为qccz123456原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qccz123456/article/details/95369115