Linux 一文详解缓冲区buffer和缓存cache

您所在的位置:网站首页 cpu缓冲区空间大小怎么看 Linux 一文详解缓冲区buffer和缓存cache

Linux 一文详解缓冲区buffer和缓存cache

2024-07-05 20:24| 来源: 网络整理| 查看: 265

上一节,我们梳理了 Linux 内存管理的基本原理,并学会了用 free 和 top 等工具,来查看系统和进程的内存使用情况。

内存和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是缓存,两者都是数据在内存中的临时存储。那么,你知道这两种"临时存储"有什么区别吗?

缓存包括两部分,一部分是磁盘读取文件的页缓存,用来缓存从磁盘读取的数据,可以加快以后再次访问的速度。另一部分,则是 Slab 分配器中的可回收内存。缓冲区是对原始磁盘块的临时存储,用来缓存将要写入磁盘的数据。这样,内核就可以把分散的写集中起来,统一优化磁盘写入。

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 %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 记录。

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

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

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

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

案例环境

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

[root@www ~]# echo 3 > /proc/sys/vm/drop_caches [root@www ~]# free -k total used free shared buff/cache available Mem: 999696 395048 498632 12068 106016 460428 Swap: 524284 1460 522824 [root@www ~]# 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 1460 498756 0 106032 0 0 10 36 101 103 1 0 99 0 0 0 0 1460 498740 0 106032 0 0 0 0 99 117 0 1 99 0 0 0 0 1460 498740 0 106032 0 0 0 0 133 133 1 0 99 0 0 0 0 1460 498740 0 106032 0 0 0 0 93 110 0 0 100 0 0 0 0 1460 498740 0 106032 0 0 0 0 108 123 0 0 100 0 0 1 0 1460 498740 0 106032 0 0 0 0 116 122 0 0 100 0 0 ^C 0 0 1460 498740 0 106032 0 0 0 0 118 130 0 1 99 0 0

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

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

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

[root@www ~]# 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 1460 498756 0 106032 0 0 10 36 101 103 1 0 99 0 0 0 0 1460 498740 0 106032 0 0 0 0 99 117 0 1 99 0 0 0 0 1460 498740 0 106032 0 0 0 0 133 133 1 0 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@www ~]# 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 1460 494064 0 112456 0 0 10 34 101 104 1 0 99 0 0 0 0 1460 494064 0 112456 0 0 0 0 79 110 0 0 100 0 0 0 0 1460 494064 0 112456 0 0 0 0 84 108 1 0 99 0 0 1 0 1460 462136 0 143004 0 0 76 0 649 131 0 58 41 1 0 2 1 1460 418292 0 186924 0 0 0 40972 872 132 0 100 0 0 0 3 0 1460 361080 0 244136 0 0 0 46104 912 143 0 100 0 0 0 3 0 1460 318448 0 286596 0 0 0 55744 870 160 0 100 0 0 0 1 0 1460 278600 0 326548 0 0 0 58180 853 163 0 100 0 0 0 2 0 1460 217628 0 387496 0 0 0 30220 958 127 0 100 0 0 0 3 0 1460 166184 0 438908 0 0 0 49676 905 171 1 99 0 0 0 2 0 1460 114476 0 490684 0 0 0 47640 895 173 0 100 0 0 0 2 0 1460 69588 0 535576 0 0 0 48652 895 166 1 99 0 0 0 3 0 1460 69632 0 535776 0 0 0 66048 837 174 0 100 0 0 0 4 1 1460 59404 0 545864 0 0 0 59404 784 168 0 100 0 0 0 4 1 1460 69108 0 536212 0 0 0 89612 787 227 0 100 0 0 0 7 2 1460 71664 0 533064 0 0 0 144420 733 210 0 82 0 18 0 4 0 1460 76464 0 528892 0 0 0 98328 651 131 0 100 0 0 0 1 1 1460 73948 0 532548 0 0 0 87052 657 191 2 98 0 0 0 [root@www ~]# free -k total used free shared buff/cache available Mem: 999696 392320 70548 12036 536828 424788

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

 在Cache 刚开始增长时,块设备 I/O很少,bi只出现了一次76KB/s,bo则只有一次34KB。而过一段时间后,才会出现大量的块设备写,比如 bo 变成了40972。当dd命令结束后,Cache 不再增长,但块设备写还会持续一段时间,并且,多次I/O写的结果加起来,才是dd 要写1G的数据。

把这个结果,跟我们刚刚了解到的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 是对文件读的页缓存"是一致的。

如果不清理缓存,直接读取,你可以看到会非常的快,因为之前写入文件就缓存起来了 863 MB/s。

[root@docker ~]# dd if=/dev/zero of=/tmp/file.txt bs=1M count=1024 1024+0 records in 1024+0 records out 1073741824 bytes (1.1 GB) copied, 2.60613 s, 412 MB/s [root@docker ~]# dd if=/tmp/file.txt of=/dev/null 2097152+0 records in 2097152+0 records out 1073741824 bytes (1.1 GB) copied, 1.24449 s, 863 MB/s

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

首先,回到第二个终端,运行下面的命令。清理缓存后,从磁盘分区/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 文件系统也是我们的好帮手。它为我们呈现了系统内部的运行状态,同时也是很多性能工具的数据来源,是辅助排查性能问题的好方法。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3