从 Buffer 和 Cache 到 Linux 的 PageCache 和 BufferCahe

导读

  • 可以了解到 Buffer 和 Cache 的区别
  • 传统 IO 模型中对 Buffer 和 Cache 的使用
  • Linux 的 PageCache 和 BufferCahe 是什么以及它们的关系

起因

  • hello,大家好,我是 Lorin,事情起因源于在知乎看到一篇问答: Cache 和 Buffer 的区别是什么?

企业微信截图_16923347739903.png

  • 下面有很多知乎博主进行了回答,下面摘抄了几个个人觉得比较好的回答:

企业微信截图_16923349618621.png

企业微信截图_1692335068454.png

企业微信截图_16923351012800.png

Buffer 和 Cache

  • 通过大家的回答和自己查阅资料,以下是个人见解:
  • Cache 标准可以翻译为缓存或者快取,主要用于解决系统两端性能不匹配的问题,利用空间局部性原理和时间局部性原理加快访问速度,提高性能。比如我们常见的CPU多级缓存;通俗的讲,比如我们每天出门会带个包,包的空间有限,我们会选取经常使用的物品放在其中,避免我们身上没有带这个物品而需要辛辛苦苦跑回家去取,这个包相当于缓存。
  • Buffer 翻译为缓冲区,更偏向解决生产者和消费者速率不匹配的问题,平滑整个传输过程减少延迟,提高性能。比如我们常见的磁盘缓冲区;通俗的例子,比如以前还小的时候,大家还没有自来水,水都来自山上,但每到晚上用水量就会剧增,怎么办呢?比如大家都会修建蓄水池来储水,避免没有晚上水不够用,这个蓄水池就称为缓冲区。

企业微信截图_1692352919646.png

  • Cache 和 Buffer 在系统实现中,特别是在 IO 系统中,Cache 和 Buffer 是一体的,同时具备缓冲区和缓存的作用,比如内核缓冲区就同时具备缓存和缓冲区的作用,提高了我们文件访问速率,同时也避免频繁的写入数据到磁盘中,起到了保护磁盘和提高文件写入速度的作用。

传统 IO 模型中的 Buffer 和 Cache

企业微信截图_16923530711803.png

  • 传统 IO 模型涉及到 Buffer 和 Cache的地方主要有以下几个地方:用户缓冲区、内核缓冲区、磁盘缓冲区,我们下面一一进行简单介绍:

用户缓冲区

  • 所谓用户缓冲区就是用户进程在读取文件等场景时,常常先申请一块内存存储我们读取的数据,我们称为 Buffer,每次 read 调用将 Buffer 填充满之后再进行下一次 read 调用,从而减少 read 系统调用,降低系统调用用户态和内核态切换的开销。

内核缓冲区

  • 数据预读及缓存:当一个用户进程需要读取磁盘数据时,不会直接读取磁盘而是会先读取内核缓存区,若内核缓冲区存在数据,则直接内核缓冲区的数据;若内核缓冲区中不存在,则请求从磁盘读取,读取时不仅会读取我们需要的数据,还会预读相邻的数据(空间局部性原理)。
  • 延时回写:当一个用户进程需要写数据时,数据不会直接写入到磁盘中,而是将数据写入到内核缓冲区,在适当的时机,内核会将内核缓冲区的数据写入到磁盘中,避免频繁的磁盘的写入以及提高写入速度。
  • 从上面我们可以看出,内核缓冲区不仅仅具有 Buffer 的作用,同时也具备 Cache 的作用。

内核缓冲区的写入时机

  • 缓冲区满: 当内核缓冲区达到一定的阈值或满载时,操作系统会触发将数据写入磁盘,以释放缓冲区空间。
  • 定期刷新: 操作系统可能会定期进行刷新,将内核缓冲区中的数据写入磁盘,以确保数据的一致性。这种刷新通常通过一些后台进程或线程来完成。
  • 文件关闭: 当一个文件被关闭时,操作系统会将该文件的内核缓冲区中的数据写入磁盘,以确保数据的持久性。
  • sync() 和 fsync(): 程序员可以显式地调用sync()或fsync()系统调用来要求操作系统将缓冲区中的数据立即写入磁盘。fsync()将特定文件的缓冲区数据刷新到磁盘,而sync()会刷新所有文件的缓冲区数据。

不使用内核缓冲区

  • 内核缓冲区虽然带来了许多好处,但是并不是适用所有场景,比如可能造成数据丢失、大文件读写等并不适合使用内核缓冲区,这时候我们可以使用一些方法不使用内核缓冲区:
// O_DIRECT:对于类UNIX系统,可以使用O_DIRECT选项来绕过内核缓冲区,直接将数据写入磁盘。这需要在打开文件时使用特定的标志。
#include <fcntl.h>

int main() {
    int fd = open("output.txt", O_WRONLY | O_DIRECT);
    // 写入数据到文件描述符 fd
    close(fd);
    return 0;
}

一个好问题:为什么有了内核缓冲区还需要应用层的缓存?

  • 简单来说,操作系统提供的内核缓冲区是一个通用选择,无法适应我们应用层的个性化场景。仅对于顺序读写可以很好的进行处理,比如 kafka,但实际上对于大多数应用层的热点数据都分布不均。
  • 例如:MySQL的innoDB缓存,如果采用OS的缓存策略,来一次全表扫描那么就可以让InnoDB辛辛苦苦热起来的数据冷了。但是InnoDB自己维护缓存情况下,就可以处理得很好,例如MySQL的InnoDB会对缓冲数据拆分为young以及old数据;会在整个缓存空间中腾出3/8的数据来用缓存这种多次访问的热点数据;这样全表扫描情况下,至少大多数热点数据还在内存中。

磁盘缓冲区

  • 磁盘缓冲区是硬盘与PCI总线之间的容量固定的硬件。同样是一种优化磁盘 I/O 性能的重要技术,通过减少磁盘访问次数和优化数据写入方式,提高了计算机系统的整体效率和响应速度。
  • 这里留一个小问题,为什么有了内核缓冲区还需要磁盘缓冲区,猜测是和上面的内核缓冲区一样的原因,留到后面有机会解答。
// 待考证
一、它是容量固定的硬件,而不像内核缓冲区是可以由操作系统在内存中动态分配的。
二、它对性能的影响大大超过磁盘缓存对性能的影响,因为如果没有缓冲区,就会要求每传一个字(通常是4字节)就需要读一次磁盘或写一次磁盘。

Linux 的 PageCache 和 BufferCahe

  • 以前在各种文章中经常看到 PageCache 和 BufferCahe 两个概念,但具体是什么不是很了解,趁着上面这个问题就一起进行了简单了解。实际上 PageCache 和 BufferCahe 实际上是 Linux 文件系统发展中不同时期的产物。

仅有 Buffer Cache

  • 在Linux-0.11版本的代码中,buffer cache是完全独立的实现,甚至都还没有基于page作为内存单元,而是以原始指针的系形式出现。每一个block sector,在kernel内部对应一个独立的buffer cache单元,这个buffer cache单元通过buffer head来描述:

8d40855de8d6e4173a9b3123f2afe271.png

Page Cache、Buffer Cache两者并存

  • 到Linux-2.2版本时,磁盘文件访问的高速缓冲仍然是缓冲区高速缓冲(Buffer Cache)。其访问模式与上面Linux-0.11版本的访问逻辑基本类似。但此时,Buffer Cache已基于page来分配内存,buffer_head内部,已经有了关于所在page的一些信息:

7892a6decc1696dba09a11023631cc2f.png

Page Cache 的用途

  • page cache was used to cache pages of files mapped with mmap MAP_FILE among other things.(即用于 MMAP)
  • 此时, Page Cache和Buffer Cache的关系如下图所示:

32e9d23adb5dcce3148c86518df80821.png

  • 从上图我们可以看出,Page Cache仅负责其中mmap部分的处理,而Buffer Cache实际上负责所有对磁盘的IO访问。但是我们会发现一个问题:write绕过了Page Cache,这里导致了一个同步问题。当write发生时,有效数据是在Buffer Cache中,而不是在Page Cache中。这就导致mmap访问的文件数据可能存在不一致问题。为了解决这个问题,所有基于磁盘文件系统的write,都需要调用 update_vm_cache() 函数,该操作会修改write相关Buffer Cache对应的Page Cache。

Page Cache、Buffer Cache两者融合

  • 为了解决上述Page Cache、Buffer Cache分离设计的弊端,Linux-2.4版本对Page Cache、Buffer Cache的实现进行了融合,融合后的Buffer Cache不再以独立的形式存在,Buffer Cache的内容,直接存在于Page Cache中,同时,保留了对Buffer Cache的描述符单元:buffer_head

4c25c4103535b21b565fd22c07bbc838.png

free 命令 Buffer Cache 解读

企业微信截图_16923616648101.png

参考

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lorin 洛林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值