Linux 缓冲区buffer和缓存cache

内存和CPU的关系非常紧密,而内存管理本身也是很复杂的机制,所以感觉知识很硬核、很难啃,都是正常的。但还是那句话,初学时不用非得理解所有内容,继续往后学,多理解相关的概念并配合一定的实践之后,再回头复习往往会容易不少。当然,基本功不容放弃。先来回顾一下系统的内存使用情况,比如下面这个 free输出界面∶

[root@localhost ~]# free
              total        used        free      shared  buff/cache   available
Mem:        3091140      242976     2413288       17116      434876     2550136
Swap:        524284        1748      522536

 显然,这个界面包含了物理内存Mem 和交换分区 Swap的具体使用情况,比如总内存、已用内存、缓存、可用内存等。其中缓存是 Buffer和 Cache 两部分的总和。

这里的大部分指标都比较容易理解,但 Buffer和Cache 可能不太好区分。从字面上来说,Buffer是缓冲区,而Cache是缓存,两者都是数据在内存中的临时存储。那么,你知道这两种"临时存储"有什么区别吗?

 

free 数据的来源


用 man 命令查询 free的文档,就可以找到对应指标的详细说明。比如,我们执行 man free,就可以看到下面这个界面。

DESCRIPTION
       free  displays the total amount of free and used physical and swap memory in the system, as well as the buffers and
       caches used by the kernel. The information is gathered by parsing /proc/meminfo. The displayed columns are:

       total  Total installed memory (MemTotal and SwapTotal in /proc/meminfo)

       used   Used memory (calculated as total - free - buffers - cache)

       free   Unused memory (MemFree and SwapFree in /proc/meminfo)

       shared Memory used (mostly) by tmpfs (Shmem in /proc/meminfo, available on kernels 2.6.32, displayed as zero if not
              available)

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

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

       buff/cache
              Sum of buffers and cache

       available
              Estimation  of how much memory is available for starting new applications, without swapping. Unlike the data
              provided by the cache or free fields, this field takes into  account  page  cache  and  also  that  not  all
              reclaimable  memory slabs will be reclaimed due to items being in use (MemAvailable in /proc/meminfo, avail‐
              able on kernels 3.14, emulated on kernels 2.6.27+, otherwise the same as free)

从 free的手册中,你可以看到 buffer和 cache的说明。

● Buffers 是内核缓冲区用到的内存,对应的是/proc/meminfo 中的 Buffers 值。

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

这里的说明告诉我们,这些数值都来自/proc/meminfo,但更具体的Buffers、Cached和SReclaimable的含义还是没有说清楚。

那么,有没有更简单、更准确的方法,来查询它们的含义呢?

proc 文件系统


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

proc 文件系统同时也是很多性能工具的最终数据来源。比如我们刚才看到的 free,就是通过读取/proc/meminfo,得到内存的使用情况。

既然Buffers、Cached、SReclaimable这几个指标不容易理解,那我们还得继续查 proc 文件系统,获取它们的详细定义。执行man proc,你就可以得到proc 文件系统的详细文档。

注意这个文档比较长,你最好搜索一下(比如搜索 meminfo),以便更快定位到内存部分。

通过这个文档,我们可以看到∶

● Buffers是对原始磁盘块的临时存储,也就是用来缓存磁盘的数据,通常不会特别大(20MB 左右)这样内核就可以把分散的写集中起来,统一优化磁盘的写入,比如可以把多次小的写合并成单次大的写等等。

● Cached是从磁盘读取文件的页缓存,也就是用来缓存从文件读取的数据。这样,下次访问这些文件数据时,就可以直接从内存中快速获取,而不需要再次访问缓慢的磁盘。

● SReclaimable是Slab的一部分。Slab 包括两部分,其中的可回收部分,用SReclaimable 记录,而不可回收部分,用 SUnreclaim 记录。

好了,我们终于找到了这三个指标的详细定义。到这里,你是不是长舒一口气,满意地想着,总算弄明白Buffer和Cache了。不过,知道这个定义就真的理解了吗?这里我给你提了两个问题,你先想想能不能回答出来。

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

第二个问题,文档中提到,Cache是对从文件读取数据的缓存,那么它是不是也会缓存写文件的数据呢?

为了解答这两个问题,接下来用几个案例来展示,Buffer 和Cache在不同场景下的使用情况。

案例环境


 预先安装 systat 包,之所以要安装 systat,是因为我们要用到vmstat,来观察 Buffer和Cache的变化情况。虽然从/proc/meminfo 里也可以读到相同的结果,但毕竟还是 vmstat 的结果更加直观。. 另外,这几个案例使用了 dd来模拟磁盘和文件的/O,所以我们也需要观测I/O的变化情况。最后一步,为了减少缓存的影响,记得在第一个终端中,运行下面的命令来清理系统缓存∶

[root@localhost ~]# echo 3 > /proc/sys/vm/drop_caches 
[root@localhost ~]# 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
 2  0      0 602264      0  83216    0    0   172    85  158  211  1  2 96  0  0
 0  0      0 602264      0  83216    0    0     0     0  127  161  0  0 100  0  0
 0  0      0 602264      0  83216    0    0     0     0  126  154  0  0 100  0  0
 0  0      0 602264      0  83216    0    0     0     0  135  162  0  1 99  0  0
^C

这里的/proc/sys/m/drop_caches,就是通过proc文件系统修改内核行为的一个示例,写入3表示清理文件页、目录项、Inodes等各种缓存。

场景 1∶磁盘和文件写案例


首先,在第一个终端,运行下面这个vmstat命令∶

[root@localhost ~]# 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
 2  0      0 602264      0  83216    0    0   172    85  158  211  1  2 96  0  0
 0  0      0 602264      0  83216    0    0     0     0  127  161  0  0 100  0  0
 0  0      0 602264      0  83216    0    0     0     0  126  154  0  0 100  0  0
 0  0      0 602264      0  83216    0    0     0     0  135  162  0  1 99  0  0

输出界面里,内存部分的 buff和 cache,以及 io 部分的bi和bo 就是我们要关注的重点。

● buff和 cache就是我们前面看到的Buffers和Cache,单位是 KB。

● bi和 bo 则分别表示块设备读取和写入的大小,单位为块/秒。因为Linux中块的大小是1KB,所以这个单位也就等价于 KB/s。

正常情况下,空闲系统中,你应该看到的是,这几个值在多次结果中一直保持不变。接下来,到第二个终端执行 dd 命令,通过读取随机设备,生成一个 1000MB大小的文件∶

[root@localhost ~]# dd if=/dev/zero of=/tmp/file.txt bs=1M count=1000
1000+0 records in
1000+0 records out
1048576000 bytes (1.0 GB) copied, 21.4168 s, 49.0 MB/s

观察Buffer,Cache的变化情况

[root@localhost ~]# 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 584528      4  94912    0    0   120    58  139  183  1  2 97  0  0
 0  0      0 584528      4  94912    0    0     0     0  103  123  0  1 99  0  0
 0  0      0 584528      4  94912    0    0     0     0   97  113  0  0 100  0  0
 5  2      0 372300      4 305916    0    0    76 162324  801  212  0 82  0 18  0
 5  0      0 363932      4 314192    0    0     0 50734  254   72  0 82  0 18  0
 3  0      0 304488      4 373756    0    0     0 20480  626  114  0 100  0  0  0
 5  0      0 267992      4 410268    0    0     0 26112  447   96  3 97  0  0  0
 6  0      0 246492      4 431616    0    0     0 35328  324   82  0 100  0  0  0
 6  0      0 226048      4 451964    0    0     0 52396  366   77  0 100  0  0  0
 3  0      0 200996      4 477104    0    0     0     0  470  105  0 100  0  0  0
 2  0      0 163456      4 514776    0    0     0 20480  600  103  0 100  0  0  0
 2  0      0 144140      4 533992    0    0     0 20480  410  102  0 100  0  0  0
 2  0      0 114564      4 563576    0    0     0 20480  588  101  0 100  0  0  0
 2  0      0  91096      4 586968    0    0     0 20480  448  103  0 100  0  0  0
 8  0      0  88456      4 589736    0    0     0 51928  198   59  0 100  0  0  0
 7  0      0  53572      0 624772    0    0     0 12800  784  130  1 99  0  0  0
 6  0      0  65512      0 612852    0    0     0 48640  189   73  0 100  0  0  0

通过观察 vmstat 的输出,我们发现,在dd命令运行时,Cache在不停地增长,而Buffer 基本保持不变。再进一步观察 I/O的情况,你会看到:

● 在Cache 刚开始增长时,块设备 I/O很少,bi只出现了一次120KB/s,bo则只有一次58KB。而过一段时间后,才会出现大量的块设备写,比如 bo 变成了162324。

● 当dd命令结束后,Cache 不再增长,但块设备写还会持续一段时间,并且,多次I/O写的结果加起来,才是dd 要写的1000M 的数据。

把这个结果,跟我们刚刚了解到的Cache 的定义做个对比,你可能会有点晕乎。为什么前面说 Cache 是文件读的页缓存,怎么现在写文件也有它的份?

这个疑问,我们暂且先记下来,接着再来看另一个磁盘写的案例。两个案例结束后,我们再统一进行分析。

不过,对于接下来的案例,我必须强调一点∶

下面的命令对环境要求很高,需要你的系统配置多块磁盘,并且磁盘分区/dev/sdb1还要处于未使用状态。如果你只有一块磁盘,千万不要尝试,否则将会对你的磁盘分区造成损坏。运行下面的命令。清理缓存后,向磁盘分区/dev/sdb1写入 2GB的随机数据∶

#先清理缓存
[root@localhost ~]# echo 3 >/proc/sys/vm/drop_caches 
[root@localhost ~]# 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
 2  0      0 605048      0  79108    0    0    90   541  131  166  1  1 98  0  0
 0  0      0 605048      0  79108    0    0     0     0  141  166  0  0 100  0  0
 0  0      0 605048      0  79108    0    0     0     0  134  153  0  0 100  0  0
 0  0      0 605048      0  79108    0    0     0     0  134  155  0  0 100  0  0
 0  0      0 605048      0  79108    0    0     0     0  138  159  0  0 100  0  0

观察内存和I/O的变化

[root@localhost ~]# fdisk -l | grep sdb
Disk /dev/sdb: 2147 MB, 2147483648 bytes, 4194304 sectors
[root@localhost ~]# mkfs.xfs /dev/sdb
meta-data=/dev/sdb               isize=512    agcount=4, agsize=131072 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=0, sparse=0
data     =                       bsize=4096   blocks=524288, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
log      =internal log           bsize=4096   blocks=2560, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
#使用dd命令向磁盘分区当/dev/sdb中写入数据写入2G数据
[root@localhost ~]# dd if=/dev/zero of=/dev/sdb bs=1M count=2048
2048+0 records in
2048+0 records out
2147483648 bytes (2.1 GB) copied, 15.7479 s, 136 MB/s



#在去另外一个终端观察内存I/O情况
[root@localhost ~]# 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
 2  0      0 580712      0  81092    0    0   334    51  169  285  2  4 94  0  0
 0  0      0 580712      0  81092    0    0     0     4   89  108  0  1 99  0  0
 0  0      0 580712      0  81092    0    0     0     0   70   82  0  0 100  0  0
 1  0      0 580712      0  81092    0    0     0     0   85   94  0  1 99  0  0
 0  0      0 580712      0  81092    0    0     0     0  104  113  0  0 100  0  0
 0  0      0 580712      0  81092    0    0     0     0   85   96  0  1 99  0  0
 1  0      0 549456  29312  81864    0    0    80     0  233  122  1 14 85  0  0
 1  0      0 488384  88840  83516    0    0     0 40960 1058   93  0 100  0  0  0
 1  0      0 439812 136116  84684    0    0     0 61440 1127  117  0 100  0  0  0
 1  0      0 391936 182636  85952    0    0     0 40960 1094   96  0 100  0  0  0
 1  0      0 354324 219292  86944    0    0     0 40960 1083   98  0 100  0  0  0
 2  0      0 315108 257308  88148    0    0     0 37556 1112  108  0 100  0  0  0
 1  0      0 271784 299484  89320    0    0     0 58812 1176  109  0 100  0  0  0
 1  0      0 225988 344164  90472    0    0     0 20480 1105   97  0 100  0  0  0
 1  0      0 191380 377900  91364    0    0     0 40960 1141  109  0 100  0  0  0
 1  0      0 158392 409840  92292    0    0     0 20480 1096   93  0 100  0  0  0
 1  0      0 127064 440364  93152    0    0     0 40960 1138  114  0 100  0  0  0

从这里你会看到,虽然同是写数据,写磁盘跟写文件的现象还是不同的。写磁盘时(也就是bo 大于0时),Buffer和Cache都在增长,但显然 Buffer的增长快得多。这说明,写磁盘用到了大量的 Buffer,这跟我们在文档中查到的定义是一样的。

对比两个案例,我们发现,写文件时会用到Cache 缓存数据,而写磁盘则会用到 Buffer来缓存数据。所以,回到刚刚的问题,虽然文档上只提到,Cache是文件读的缓存,但实际上,Cache 也会缓存写文件时的数据。

场景2∶磁盘和文件读案例


了解了磁盘和文件写的情况,我们再反过来想,磁盘和文件读的时候,又是怎样的呢?运行下面的命令。清理缓存后,从文件/tmp/file中,读取数据写入空设备∶

[root@localhost ~]# echo 3 > /proc/sys/vm/drop_caches 
[root@localhost ~]# dd if=/tmp/file.txt  of=/dev/null 
4194304+0 records in
4194304+0 records out
2147483648 bytes (2.1 GB) copied, 8.45549 s, 254 MB/s

来到另外一个终端

[root@localhost ~]# 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
 2  0   2288 601176      0  43060    0    0    53   244   71   63  1  1 98  0  0
 0  0   2288 601160      0  43060    0    0     0     0   72   78  0  0 100  0  0
 1  0   2288 482260      0 161728    0    0 118684     0  793  143  5 48 46  0  0
 2  0   2288 258256      0 382928    0    0 221184     0 1369  132  8 92  0  0  0
 2  0   2288  61076      0 583020    0    0 233472     0 1422  116  9 91  0  0  0
 1  0   2288  64924      0 577064    0    0 249856     0 1497  143 11 89  0  0  0
 1  0   2288  64672      0 578972    0    0 274432     0 1546  134 12 88  0  0  0
 1  0   2288  53744      0 590512    0    0 249856     0 1498  137 11 89  0  0  0
 1  0   2288  63624      0 580556    0    0 262144     0 1518  134 13 87  0  0  0
 2  0   2288  55732      0 588352    0    0 262144     0 1514  139 12 88  0  0  0
 0  0   2288  66156      0 577632    0    0 226664     0 1376  172 10 79 11  0  0
 1  0   2288  66156      0 577632    0    0     0     0   82   84  0  0 100  0  0
 0  0   2288  66156      0 577632    0    0     0     0   88   88  1  0 99  0  0

观察vmstat的输出,你会发现读取文件时(也就是bi大于0时),Buffer保持不变,而Cache则在不停增长。这跟我们查到的定义"Cache 是对文件读的页缓存"是一致的。

那么,磁盘读又是什么情况呢?  再运行第二个案例来看看。

首先,回到第二个终端,运行下面的命令。清理缓存后,从磁盘分区/dev/sda1中读取数据,写入空设备∶

[root@localhost ~]# dd if=/dev/sdb of=/dev/null  bs=1M count=1024
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 3.10085 s, 346 MB/s

在另外一个终端观察内存和I/O变化 

[root@localhost ~]# 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
 2  0   2244 591464      0  51796    0    0    52   126   71   63  1  1 98  0  0
 0  0   2244 591480      0  51796    0    0     0     0   48   58  0  1 99  0  0
 0  0   2244 591496      0  51796    0    0     0     3   45   51  0  0 100  0  0
 1  0   2244 502144  88064  51876    0    0 88140     0  596   59  0 39 61  0  0
 1  0   2244 293260 296960  51848    0    0 208896     0 1414   36  0 100  0  0  0
 1  0   2244  88420 501760  51884    0    0 204800     0 1408   41  0 100  0  0  0

观察 vmstat的输出,你会发现读磁盘时(也就是bi大于0时),Buffer和Cache都在增长,但显然Buffer的增长快很多。这说明读磁盘时,数据缓存到了 Buffer中。

可以对比得出这个结论∶读文件时数据会缓存到Cache中,而读磁盘时数据会缓存到Buffer 中。

到这里你应该发现了,虽然文档提供了对Buffer和Cache的说明,但是仍不能覆盖到所有的细节。比如说,今天我们了解到的这两点∶

●  Buffer 既可以用作"将要写入磁盘数据的缓存",也可以用作"从磁盘读取数据的缓存"。

●  Cache 既可以用作"从文件读取数据的页缓存",也可以用作"写文件的页缓存". 这样,我们就回答了案例开始前的两个问题。

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

今天,我们一起探索了内存性能中Buffer和Cache的详细含义。Buffer和Cache 分别缓存磁盘和文件系统的读写数据。

●  从写的角度来说,不仅可以优化磁盘和文件的写入,对应用程序也有好处,应用程序可以在数据真正落盘前,就返回去做其他工作。

●  从读的角度来说,既可以加速读取那些需要频繁访问的数据,也降低了频繁I/O对磁盘的压力。

proc 文件系统也是我们的好帮手。它为我们呈现了系统内部的运行状态,同时也是很多性能工具的数据来源,是辅助排查性能问题的好方法。

猜你喜欢

转载自blog.csdn.net/qq_34556414/article/details/108049810