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 对磁盘的压力。

实际案例

  磁盘是块设备,可以划分为不同的分区,在分区之上再创建文件系统,挂载到某个目录,之后才可以在这个目录中读写文件。

  在读写普通文件时,I/O 请求会首先经过文件系统,然后由文件系统负责,来与磁盘进行交互。而在读写块设备文件时,会跳过文件系统,直接与磁盘交互,也就是所谓的“裸 I/O”。所以文件系统管理的缓存,就是 Cache。而裸磁盘的缓存,用的正是 Buffer。

  所以按原理来说,读写文件先经过Cache,再经过Buffer,但后期 Linux 经过优化,将Cache指针直接指向Buffer,读写文件就绕过Buffer,只存在Cache。

  (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 入手。

  RSS 表示常驻内存,把进程用到的共享内存也算了进去。所以,直接累加会导致共享内存被重复计算,不能得到准确的答案。但是可以通过每个进程的 PSS(proportional set size),指把共享内存平分到各个进程后,再加上进程本身的非共享内存大小的和。

  比如:一个进程的非共享内存为 1000 页,它和另一个进程的共享进程也是 1000 页,那么它的 PSS=1000/2+1000=1500 页。grep Pss /proc/[1-9]*/smaps | awk '{total+=$2}; END {printf "%d kB\n", total }'

Linux性能优化从入门到实战:09 内存篇:Buffer和Cache的更多相关文章

  1. Linux性能优化从入门到实战:01 Linux性能优化学习路线

      我通过阅读各种相关书籍,从操作系统原理.到 Linux内核,再到硬件驱动程序等等.   把观察到的性能问题跟系统原理关联起来,特别是把系统从应用程序.库函数.系统调用.再到内核和硬件等不同的层级贯 ...

  2. Linux性能优化从入门到实战:16 文件系统篇:总结磁盘I/O指标/工具、问题定位和调优

    (1)磁盘 I/O 性能指标 文件系统和磁盘 I/O 指标对应的工具 文件系统和磁盘 I/O 工具对应的指标 (2)磁盘 I/O 问题定位分析思路 (3)I/O 性能优化思路 Step 1:首先采用 ...

  3. Linux性能优化从入门到实战:07 CPU篇:CPU性能优化方法

    性能优化方法论   动手优化性能之前,需要明确以下三个问题:   (1)如何评估性能优化的效果? 确定性能的量化指标.测试优化前的性能指标.测试优化后的性能指标.   量化指标的选择.至少要从应用程序 ...

  4. Linux性能优化从入门到实战:12 内存篇:Swap 基础

    内存资源紧张时,可能导致的结果 (1)OOM 杀死大内存CPU利用率又低的进程(系统内存耗尽的情况下才生效:OOM 触发的时机是基于虚拟内存,即进程在申请内存时,如果申请的虚拟内存加上服务器实际已用的 ...

  5. Linux性能优化从入门到实战:08 内存篇:内存基础

    内存主要用来存储系统和应用程序的指令.数据.缓存等. 内存映射   物理内存也称为主存,动态随机访问内存(DRAM).只有内核才可以直接访问物理内存.   Linux 内核给每个进程都提供了一个独立的 ...

  6. Linux性能优化从入门到实战:17 网络篇:网络基础

    网络模型 为了解决网络互联中异构设备的兼容性问题,并解耦复杂的网络包处理流程,国际标准化组织制定了开放式系统互联通信参考模型(Open System Interconnection Reference ...

  7. Linux性能优化从入门到实战:15 文件系统篇:磁盘 I/O

    磁盘   磁盘是可以持久化存储的设备,按照存储介质来分类:   (1)机械磁盘(硬盘驱动器,Hard Disk Driver,HDD),主要由盘片和读写磁头组成,数据就存储在盘片的环状磁道中.在读写数 ...

  8. Linux性能优化从入门到实战:10 内存篇:如何利用Buffer和Cache优化程序的运行效率?

    缓存命中率   缓存命中率,是指直接通过缓存获取数据的请求次数,占所有数据请求次数的百分比,可以衡量缓存使用的好坏.命中率越高,表示使用缓存带来的收益越高,应用程序的性能也就越好.   实际上,缓存是 ...

  9. Linux性能优化从入门到实战:04 CPU篇:CPU使用率

      CPU使用率是单位时间内CPU使用情况的统计,以百分比方式展示. $ top top - 11:46:45 up 7 days, 11:52, 1 user, load average: 0.00 ...

随机推荐

  1. ES的聚合操作

    构建数据: ​    @Test    public void createIndex(){        /**         * 创建索引         * */        client. ...

  2. Vue点击切换Class变化,实现Active当前样式

    刚自学Vue不久,所以还不太熟,所以直接上代码. 一.先在data里增加一个变量,用来储存当前点击的元素 data() { return { activeClass: -1, // 0为默认选择第一个 ...

  3. 顶级域名、一级域名、二级域名与IP

    转自:https://blog.csdn.net/qq_38071429/article/details/80339091 域名:可分三级,一级域名,二级域名,三级域名.是由一串字符+域名后缀组成,我 ...

  4. Zipf's law

    w https://www.bing.com/knows/search?q=马太效应&mkt=zh-cn&FORM=BKACAI 马太效应(Matthew Effect),指强者愈强. ...

  5. 记录新建dorado项目更新规则中报错

    异常: Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Er ...

  6. .Net Core 使用 System.Drawing.Common 在CentOS下报错

    .Net Core控制台项目,添加了 System.Drawing.Common 引用 #locate libdl /usr/lib64/libdl-2.17.so /usr/lib64/libdl. ...

  7. 发邮件--yagmail模块

    准备工作:1.在你的邮箱设置里面打开smtp服务(若有的话)2.开启邮箱授权码,记住这个授权码(连接邮箱服务时用) 1.安装yagmail模块pip install yagmail2.举例:impor ...

  8. 【ABAP系列】SAP ALV 导出报表数据 始终使用选定的格式”,一旦勾上,就再也不会弹出选择框了。

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ALV 导出报表数据 始 ...

  9. c++中关于类的长度的猜想

    在无意中,我偶然发现了类的长度并不是由函数的类型及个数决定,也并非是2的倍数.4的倍数. 在翻阅资料中,我得出了一些我认为可能的猜想. 我们先来看一串代码 #include<iostream&g ...

  10. VSphere服务器ESXI4.1.0设置虚拟主机来电开机自启动

    vSphere服务器ESXI设置虚拟主机来电自启动 首先查看我自己VMware vSphere版本为4.1.0(需要在虚拟主机电源为关闭状态下编辑) 然后双击主机,点击配置---虚拟机启动/关机 点击 ...