mmap为什么比read/write快(兼论buffercache和pagecache)

mmap为什

参考文献:

从内核文件系统看文件读写过程

 

《mmap是什么》

 

正文:

 

      首先说一下文件系统,Linux文件系统的三层结构想必大家多少都了解一些,每个进程中都有一个用户文件描述符表,表项指向一个全局的文件表中的某个表项,文件表表项有一个指向内存inode的指针,每个inode唯一标识一个文件。如果同时有多个进程打开同一文件,他们的用户文件描述符表项指向不同的文件表项,但是这些文件表项会指向同一个inode。

 

     此时又会引出另外一个东东:page cache。内核会为每个文件单独维护一个page cache,用户进程对于文件的大多数读写操作会直接作用到page cache上,内核会选择在适当的时候将page cache中的内容写到磁盘上(当然我们可以手工fsync控制回写),这样可以大大减少磁盘的访问次数,从而提高性能。Page cache是linux内核文件访问过程中很重要的数据结构,page cache中会保存用户进程访问过得该文件的内容,这些内容以页为单位保存在内存中,当用户需要访问文件中的某个偏移量上的数据时,内核会以偏移量为索引,找到相应的内存页,如果该页没有读入内存,则需要访问磁盘读取数据。为了提高页得查询速度同时节省page cache数据结构占用的内存,linux内核使用树来保存page cache中的页。

 

     在了解了以上的基础之后,我们就来比较一下mmap和read/write的区别,先说一下read/write系统调用,read/write系统调用会有以下的操作:

 

  1. 访问文件,这涉及到用户态到内核态的转换
  2. 读取硬盘文件中的对应数据,内核会采用预读的方式,比如我们需要访问100字节,内核实际会将按照4KB(内存页的大小)存储在page cache中
  3. 将read中需要的数据,从page cache中拷贝到用户缓冲区中

 

     整个过程还是比较艰辛的,基本上涉及到用户内核态的切换,还有就是数据拷贝接下来继续说mmap吧,mmap系统调用是将硬盘文件映射到用内存中,说的底层一些是将page cache中的页直接映射到用户进程地址空间中,从而进程可以直接访问自身地址空间的虚拟地址来访问page cache中的页,这样会并涉及page cache到用户缓冲区之间的拷贝,mmap系统调用与read/write调用的区别在于:

 

  1. mmap只需要一次系统调用,后续操作不需要系统调用
  2. 访问的数据不需要在page cache和用户缓冲区之间拷贝

 

     从上所述,当频繁对一个文件进行读取操作时,mmap会比read高效一些。

 

     最后再说一下page cache的话题,从上面所说我们从磁盘文件中读取的内容都会存在page cache中,但当我们关闭这个文件时,page cache中内容会立马释放掉吗?答案是否,磁盘的读取速度比内存慢太多,如果能命中page cache可以显著提升性能,万一后续又有对这个文件的操作,系统就可以很快速的响应。当然,这些文件内容也不是一直存在page cache中的,一般只要系统有空闲物理内存,内核都会拿来当缓存使用,但当物理内存不够用,内存会清理出部分page cache应急,这也就是告诉我们程序对于物理内存的使用能省则省,交给内核使用,作用很大。

 

     还有就是普通的write调用只是将数据写到page cache中,并将其标记为dirty就返回了,磁盘I/O通常不会立即执行,这样做的好处是减少磁盘的回写次数,提供吞吐率,不足就是机器一旦意外挂掉,page cache中的数据就会丢失。一般安全性比较高的程序会在每次write之后,调用fsync立即将page cache中的内容回写到磁盘中。

 

<think>我们正在讨论PageCacheBlockI/O层的关系。根据之前的引用知识,我们可以这样理解:PageCache是操作系统内核中的磁盘缓存机制,位于文件系统层之下,BlockI/O层之上。它缓存磁盘上的文件数据块(以页为单位),以减少对物理磁盘的访问。当文件系统需要读取数据时,先检查PageCache,如果命中则直接返回;未命中则通过BlockI/O层向磁盘发起请求。写操作时,数据先写入PageCache(成为脏页),然后由内核异步刷新到底层存储。BlockI/O层是内核中管理块设备I/O操作的子系统,负责将上层(PageCache或直接I/O)的I/O请求转换为对块设备(如磁盘)的操作。它处理请求的合并、排序(通过I/O调度器)、分派到设备驱动等。两者的关系可以总结为:1.**层次关系**:PageCache位于BlockI/O层之上。当PageCache未命中时,会调用BlockI/O层从磁盘读取数据;写回脏页时也会通过BlockI/O层写入磁盘。2.**交互方式**:PageCache与BlockI/O层通过bio结构(块I/O结构体)进行交互。当需要从磁盘读取数据时,PageCache会构造bio请求并提交给BlockI/O层。3.**数据单位**:PageCache以页(通常4KB)为单位管理缓存,而BlockI/O层以扇区(通常512字节)为单位处理I/O。因此,在提交请求时,PageCache需要将页拆分为扇区请求。引用[1]提到:“BlockI/Olayer还是比较精彩的,因为一般Blocklayer再往下可能就是某个blockdevice的driver了,所以要搞清楚blockI/Olayer向下提供什么接口,这是很重要的。”这说明了BlockI/O层的重要性,它向下为设备驱动提供统一接口,向上为PageCache等提供服务。引用[2]说明了PageCache位于内核空间,用户不能直接访问,需要通过系统调用(如read/write)或内存映射(mmap)来间接访问。当使用read系统调用时,数据先从PageCache(如果存在)拷贝到用户空间缓冲区;若使用mmap,用户空间可以直接访问PageCache,避免了拷贝开销。因此,PageCacheBlockI/O层共同协作,优化磁盘I/O性能:PageCache减少了对磁盘的实际访问次数,而BlockI/O层则优化了必要的磁盘访问操作。</think>### Page Cache与Block I/O层的关系分析 #### **核心关系定位** **Page Cache与Block I/O层是协同工作的上下游关系**:Page Cache作为**内核级缓存层**位于文件系统层之下,而Block I/O层作为**物理I/O调度层**位于Page Cache之下,两者共同构成Linux I/O栈的核心组件[^1]。 ```mermaid graph TD A[文件系统层] -->|读写请求| B[Page Cache] B -->|缓存命中| A B -->|缓存未命中| C[Block I/O层] C -->|I/O调度| D[块设备驱动] D --> E[物理磁盘] ``` #### **交互机制详解** 1. **数据流动方向** - **读路径**:文件系统 → Page Cache → (未命中) → Block I/O层 → 磁盘[^1] - **写路径**:文件系统 → Page Cache → (异步回写) → Block I/O层 → 磁盘[^2] - Page Cache直接向下层提交`bio`(Block I/O)结构体请求[^1] 2. **功能分工** | 组件 | 核心职责 | 依赖关系说明 | |---------------|----------------------------------|----------------------------------| | **Page Cache**| 缓存文件数据页(4KB单位) | 依赖Block I/O层获取未缓存数据[^1]| | **Block I/O层**| 合并/排序I/O请求<br>实现电梯算法 | 为Page Cache提供物理设备访问能力 | 3. **关键协作场景** - **缓存未命中时**:Page Cache生成`bio`请求提交至Block I/O层[^1] - **脏页回写时**:Page Cache的`pdflush`线程通过Block I/O层写磁盘 - **预读机制**:Page Cache根据访问模式触发Block I/O层预取数据块[^4] #### **性能优化协作** 1. **I/O合并优化** Block I/O层将Page Cache提交的相邻页面请求合并为连续磁盘操作(如将多个4KB页合并为64KB操作)[^1] 2. **调度策略协调** - Page Cache控制**缓存时效性**(LRU淘汰策略) - Block I/O层控制**物理访问顺序**(CFQ/Deadline调度器) ```bash # 查看当前I/O调度器 $ cat /sys/block/sda/queue/scheduler [mq-deadline] kyber bfq none # 常见调度策略 ``` 3. **零拷贝支持** 通过`mmap()`系统调用,用户空间可直接访问Page Cache,避免Block I/O层的数据拷贝[^2]: ```c void *addr = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, offset); ``` #### **层级差异对比** | 特性 | Page Cache | Block I/O层 | |---------------------|--------------------------------|---------------------------------| | **操作单位** | 内存页(Page, 通常4KB) | 磁盘扇区(Sector, 通常512B) | | **核心目标** | 减少物理I/O次数 | 优化物理I/O效率 | | **数据结构** | `address_space`对象树 | `bio`请求队列 | | **用户可见性** | 通过`/proc/meminfo`暴露 | 完全内核内部机制 | > 💡 **技术本质**:Page Cache是**逻辑缓存层**,关注数据内容;Block I/O层是**物理传输层**,关注存储介质操作。两者通过`bio`结构体桥接,共同实现"缓冲-调度"二级优化体系[^1][^4]。 --- ### 相关问题 1. 为什么Linux合并了早期的Buffer CachePage Cache机制? 2. 当使用Direct I/O绕过Page Cache时,Block I/O层如何处理请求? 3. 如何通过`ftrace`工具跟踪Page Cache到Block I/O层的请求传递过程? 4. SSD的FTL(闪存转换层)与Block I/O层有哪些功能重叠与差异?[^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值