Linux 操作系统原理 — 内存 — Cache 和 Buffer
目录
无处不在的 Cache
限于存储介质的存取速率和成本,现代计算机的存储结构呈现为金字塔型。越往塔顶,存取效率越高、但成本也越高,所以容量也就越小。得益于程序访问的局部性原理,这种节省成本的做法也能取得不俗的运行效率。从存储器的层次结构以及计算机对数据的处理方式来看,上层一般作为下层的 Cache 层来使用(广义上的 Cache)。
比如寄存器缓存 CPU Cache 的数据,CPU Cache L1~L3 层视具体实现彼此缓存或直接缓存内存的数据,而内存往往缓存来自本地磁盘的数据。本文主要讨论磁盘 IO 操作,故只聚焦于 Local Disk 的访问特性和其与 DRAM 之间的数据交互。
Cache 和 Buffer 的区别在哪里?
Cache 和 Buffer 是两个不同的概念,简单的说,Cache 是加速 “读”,而 Buffer 是缓冲 “写”,前者解决读的问题,保存从磁盘上读出的数据,后者是解决写的问题,保存即将要写入到磁盘上的数据。在很多情况下,这两个名词并没有严格区分,常常把读写混合类型称为 buffer/ cache。
为什么需要缓存?
可以看到,CPU 最快,一个时钟周期是 0.3 纳秒,内存访问需要 120 纳秒,固态硬盘访问需要 50-150 微秒,传统硬盘访问需要 1-10 毫秒, 网络访问最慢,都是几十毫秒。
如果一个时钟周期如果按 1 秒算:
- 内存访问就是 6 分钟
- 固态硬盘是 2-6 天
- 传统硬盘是 1-12 个月
- 网络访问就是几年了!
如果你是 CPU,你会觉得这个世界真是慢死了!从硬盘访问数据得等待 “几天” 甚至 “几个月”!
所以存储器的层级关系出来了,存储器越往上速度越快,但是价格越来越贵, 越往下速度越慢,但是价格越来越便宜。
Linux 的缓存机制
在 Linux 系统中,为了提高文件系统性能,内核利用一部分物理内存分配出缓存区,用于缓存系统操作和数据文件,当内核收到读写的请求时,内核先去缓存区找是否有请求的数据,有就直接返回,如果没有则通过驱动程序直接操作磁盘。
-
buffers 用来缓存 metadata 及 pages,可以理解为系统缓存,例如:vi 打开一个文件。
-
cached 用来给文件做缓存,可以理解为数据块缓存,例如:dd if=/dev/zero of=/tmp/test count=1bs=1G 测试写入一个文件,就会被缓存到缓冲区中,当下一次再执行这个测试命令时,写入速度会明显很快。
-
Swap 是交换分区,即通常我们说的虚拟内存,是从硬盘中划分出的一个分区。当物理内存不够用的时候,内核就会释放缓存区(buffers/cache)里一些长时间不用的程序,然后将这些程序临时放到 Swap 中,也就是说如果物理内存和缓存区内存不够用的时候,才会用到 Swap。
缓存机制优点: 减少系统调用次数,降低 CPU 上下文切换和磁盘访问频率。
CPU上下文切换: CPU 给每个进程一定的服务时间,当时间片用完后,内核从正在运行的进程中收回处理器,同时把进程当前运行状态保存下来,然后加载下一个任务,这个过程叫做上下文切换。实质上就是被终止运行进程与待运行进程的进程切换。
查看到系统内存的使用情况:
可以看出,系统内存为 16G,Swap 内存 16G,mem free 虽然显示为 1118,因缓存的存在,不能认为系统目前内剩下这么多内存。而应该把 buffers、cached 的也算上,即 free+cached+buffers=1118+7110+430=8658,总内存再减去 8658=7314,与 buffers/cache 行中对应 free 列的 7312 和 8659 基本一致。
Page Cache 的同步机制(一致性问题)
广义上 Cache 的同步方式有两种,即 Write Through(写穿)和 Write back(写回)。从名字上就能看出这两种方式都是从写操作的不同处理方式引出的概念,单存读不存在 Cache 一致性问题。
对应到 Linux 的 Page Cache 上所谓 Write Through 就是指 write 操作将数据拷贝到 Page Cache 后立即和下层进行同步的写操作,完成下层的更新后才返回。而 Write back 正好相反,指的是写完 Page Cache 就可以返回了。Page Cache 到下层的更新操作是异步进行的。
Linux 下 Buffered IO 默认使用的是 Write back 机制,即文件操作的写只写到 Page Cache 就返回,之后 Page Cache 到磁盘的更新操作是异步进行的。Page Cache 中被修改的内存页称之为脏页(Dirty Page),脏页在特定的时候被一个叫做 pdflush(Page Dirty Flush)的内核线程写入磁盘,写入的时机和条件如下:
- 当空闲内存低于一个特定的阈值时,内核必须将脏页写回磁盘,以便释放内存。
- 当脏页在内存中驻留时间超过一个特定的阈值时,内核必须将超时的脏页写回磁盘。
- 用户进程调用 sync、fsync、fdatasync 系统调用时,内核会执行相应的写回操作。
刷新策略由以下几个参数决定(数值单位均为 1/100 秒):
# flush 每隔 5 秒执行一次
$ sysctl vm.dirty_writeback_centisecs
vm.dirty_writeback_centisecs = 500
# 内存中驻留 30 秒以上的脏数据将由 flush 在下一次执行时写入磁盘
$ sysctl vm.dirty_expire_centisecs
vm.dirty_expire_centisecs = 3000
若脏页占总物理内存 10% 以上,则触发 flush 把脏数据写回磁盘
$ sysctl vm.dirty_background_ratio
vm.dirty_background_ratio = 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
默认是写回方式,如果想指定某个文件是写穿方式呢?即写操作的可靠性压倒效率的时候,能否做到呢?当然能,除了之前提到的 fsync 之类的系统调用外,在 open 打开文件时,传入 O_SYNC 这个 flag 即可实现。
文章来源: is-cloud.blog.csdn.net,作者:范桂飓,版权归原作者所有,如需转载,请联系作者。
原文链接:is-cloud.blog.csdn.net/article/details/105896326
- 点赞
- 收藏
- 关注作者
评论(0)