前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从 Buffer 和 Cache 到 Linux 的 PageCache 和 BufferCahe

从 Buffer 和 Cache 到 Linux 的 PageCache 和 BufferCahe

原创
作者头像
Lorin 洛林
发布2024-05-12 22:53:30
1940
发布2024-05-12 22:53:30
举报
文章被收录于专栏:操作系统操作系统

导读

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

起因

  • 事情起因源于在知乎看到一篇问答: Cache 和 Buffer 的区别是什么?
  • 下面有很多知乎博主进行了回答,下面摘抄了几个个人觉得比较好的回答:

Buffer 和 Cache

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

传统 IO 模型中的 Buffer 和 Cache

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

用户缓冲区

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

内核缓冲区

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

内核缓冲区的写入时机

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

不使用内核缓冲区

  • 内核缓冲区虽然带来了许多好处,但是并不是适用所有场景,比如可能造成数据丢失、大文件读写等并不适合使用内核缓冲区,这时候我们可以使用一些方法不使用内核缓冲区:
代码语言:C
复制
// 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 性能的重要技术,通过减少磁盘访问次数和优化数据写入方式,提高了计算机系统的整体效率和响应速度。
  • 这里留一个小问题,为什么有了内核缓冲区还需要磁盘缓冲区,猜测是和上面的内核缓冲区一样的原因,留到后面有机会解答。
代码语言:txt
复制
// 待考证
一、它是容量固定的硬件,而不像内核缓冲区是可以由操作系统在内存中动态分配的。
二、它对性能的影响大大超过磁盘缓存对性能的影响,因为如果没有缓冲区,就会要求每传一个字(通常是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来描述:

Page Cache、Buffer Cache两者并存

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

Page Cache 的用途

  • page cache was used to cache pages of files mapped with mmap MAP_FILE among other things.(即用于 MMAP)
  • 此时, Page Cache和Buffer Cache的关系如下图所示:
  • 从上图我们可以看出,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

free 命令 Buffer Cache 解读

参考

个人简介

👋 你好,我是 Lorin 洛林,一位 Java 后端技术开发者!座右铭:Technology has the power to make the world a better place.

🚀 我对技术的热情是我不断学习和分享的动力。我的博客是一个关于Java生态系统、后端开发和最新技术趋势的地方。

🧠 作为一个 Java 后端技术爱好者,我不仅热衷于探索语言的新特性和技术的深度,还热衷于分享我的见解和最佳实践。我相信知识的分享和社区合作可以帮助我们共同成长。

💡 在我的博客上,你将找到关于Java核心概念、JVM 底层技术、常用框架如Spring和Mybatis 、MySQL等数据库管理、RabbitMQ、Rocketmq等消息中间件、性能优化等内容的深入文章。我也将分享一些编程技巧和解决问题的方法,以帮助你更好地掌握Java编程。

🌐 我鼓励互动和建立社区,因此请留下你的问题、建议或主题请求,让我知道你感兴趣的内容。此外,我将分享最新的互联网和技术资讯,以确保你与技术世界的最新发展保持联系。我期待与你一起在技术之路上前进,一起探讨技术世界的无限可能性。

📖 保持关注我的博客,让我们共同追求技术卓越。

我正在参与2024腾讯技术创作特训营最新征文,快来和我瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导读
  • 起因
  • Buffer 和 Cache
  • 传统 IO 模型中的 Buffer 和 Cache
    • 用户缓冲区
      • 内核缓冲区
        • 内核缓冲区的写入时机
        • 不使用内核缓冲区
        • 一个好问题:为什么有了内核缓冲区还需要应用层的缓存?
      • 磁盘缓冲区
      • Linux 的 PageCache 和 BufferCahe
        • 仅有 Buffer Cache
          • Page Cache、Buffer Cache两者并存
            • Page Cache 的用途
          • Page Cache、Buffer Cache两者融合
          • free 命令 Buffer Cache 解读
          • 参考
          • 个人简介
          相关产品与服务
          云数据库 MySQL
          腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档