block(块),page(页),buffer cache(块缓冲)区别与联系

本文探讨了Linux内核中的块缓冲与页缓存机制,详细解释了块缓冲区如何存储在专用于块管理的缓冲页中,以及每个块缓冲区与其相关联的buffer_head结构之间的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在自己的理解里,块就是用来管理磁盘空间的,就像我们在给一个磁盘建立文件系统时候,我们可以指定block_size,而页是针对内存管理,例如从磁盘读出的数据就缓存在内存页中,但突然对关buffer cache,block buffer 这些东西迷糊了,我们又说读出的数据放在内存页里边,但我们在内核代码中看到读出的每个block会对应一个由buffer_head管理的block buffer里边,那么,究竟读出的数据是放在什么的呢, page 跟block buffer又是什么关系呢??

关于blokc  vs page 的可以参考page size disk block sizeWhat is the difference between pages and blocks?blocksize vs page size,但是没解决我疑惑的答案。后来随着对内核源码的进一步学习 及参考 understanding the linux kernel ,逐渐明白了一些;

Storing Blocks in the Page Cache

In old versions of the Linux kernel, there were two different main disk caches: the page cache, which stored whole pages of disk data resulting from accesses to the contents of the disk files, and the buffer cache, which was used to keep in memory the contents of the blocks accessed by the VFS to manage the disk-based filesystems. Starting from stable version 2.4.10, the buffer cache does not really exist anymore. In fact, for reasons of efficiency, block buffers are no longer allocated individually; instead, they are stored in dedicated pages called “buffer pages,” which are kept in the page cache.Formally, abuffer pageis a page of data associated with additional descriptors called “buffer heads,” whose main purpose is to quickly locate the disk address of each individual block in the page. In fact, the chunks of data stored in a page belonging to the page cache are not necessarily adjacent on disk.

也就是说,在 内存页中,有一种叫专门用途的页面叫“缓冲区页”。专门用来放块缓冲区。而每个块缓存区由两部分组成,,缓冲区首部(用数据结构buffer_head表示)及真正的缓冲区内容(即所存储的数据,这些数据就放在刚刚说到的缓冲区页中)。在缓冲区首部中,有一个指向数据的指针和一个缓冲区长度的字段。当一个块被调入到内存中,它要被存储在一个缓冲区中。每个缓冲区与一个块对应,它相当于磁盘块在内存中的表示。而文件在内存中由file结构体表示,而磁盘块在内存中是由缓冲区来进行表示的。由于内核处理块时需要一些信息,如块属于哪个设备与块对应于哪个缓冲区。所以每个缓冲区都有一个缓冲区描述符,称为buffer_head. 它包含了内核操作缓冲区所需要的全部信息。

<span style="font-size:14px;">struct buffer_head {

    unsigned long b_state;        /* buffer state bitmap (see above) *缓冲区的状态标志/

    struct buffer_head *b_this_page;/* circular list of page's buffers *页面中缓冲区/(一般一个页面会有多个块组成,一个页面中的块是以一个循环链表组成在一起的,该字段指向下一个缓冲区首部的地址。)

    struct page *b_page;        /* the page this bh is mapped to *存储缓冲区的页面/(指向拥有该块的页面的页面描述符)

    sector_t b_blocknr;        /* start block number *逻辑块号/

    size_t b_size;            /* size of mapping *块大小/

    char *b_data;            /* pointer to data within the page *指向该块对应的数据的指针/

 

    struct block_device *b_bdev;       //对应的块设备(通常是指磁盘或者是分区)

    bh_end_io_t *b_end_io;        /* I/O completion */

     void *b_private;        /* reserved for b_end_io *I/O完成的方法/

    struct list_head b_assoc_buffers; /* associated with another mapping */

    struct address_space *b_assoc_map;    /* mapping this buffer is

                           associated with *缓冲区对应的映射,即address_space/

    atomic_t b_count;        /* users using this buffer_head *表示缓冲区的使用计数/

};</span>

buffer_head中有两个字段表示块的磁盘地址:b_bdev表示包含该块的块设备(通常是磁盘或者分区)及b_blocknr存放逻辑块号,也就是块在磁盘或分区中的编号,b_data字段表示块缓冲区数据在缓冲区页中的位置。b_state存放缓冲区的状态,例如BH_uptodate(缓冲区包含有效数据时候被置位),Bh_Dirty(缓冲区脏就置位),BH_New(如果相应的块刚被分配而还没被访问过就置位)。

只要内核必须单独访问一个块,就要涉及存放块缓冲区中的缓冲区页,并检查相应的buffer_head

一个应用实例:如果虚拟文件系统要读1024个字节的inode 块,内核并不是只分配一个单独的缓冲区,而是必须分配一整个页,从而存放4个缓冲区(假设页大小4k),这些缓冲区将存放块设备上相邻的四块数据。其中红包含所轻轻的inode块

他们之间的关系如下:(假定一个块是1K ,一个页是4K )


 在一个缓冲页内所有块缓冲区大小必须相同,因此,在80X86体系结构上,根据块的小大,一个缓冲区页可以包括1~8个缓冲区。如果一个页作为缓冲区页使用,那么与它的块缓冲区相关的所有缓冲区首部都被收集在一个单向循环链表中。page的private字段指向野种第一个块的buffer_head,每个buffer_head存放在b_this_page字段中,该字段是指向链表中的下一个缓冲区首部的指针。此外,每个buffer_head还把page的地址存放在b_page字段中,整个图如上所示。

完成!



<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.**交互方式**:PageCacheBlockI/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 CacheBlock I/O层的关系分析 #### **核心关系定位** **Page CacheBlock 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 CacheBlock I/O层的请求传递过程? 4. SSD的FTL(闪存转换层)Block I/O层有哪些功能重叠差异?[^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值