块缓存

来源:互联网 发布:淘宝上解id靠谱吗 编辑:程序博客网 时间:2024/05/01 18:46

2.4.10的稳定版本开始,缓冲区高速缓存其实就不存在了。事实上,由于效率的原因,不再单独分配块缓冲区;相反,把它们存放在叫做“缓冲区页”的专门页中,而缓冲区页保存在页高速缓存中。

每个块缓冲区都有buffer_head类型的缓冲区头描述符。该描述符包含内核必须了解的、有关如何处理块的所有信息。因此,在对所有块操作之前,内核检查缓冲区首部。缓冲区首部的字段位于/include/linux/Buffer_head.h

struct buffer_head {

      unsigned longb_state;            /*缓冲区状态标志 */

      struct buffer_head*b_this_page;  /*指向缓冲区页的链表中的下一个元素的指针 */

      struct page*b_page;              /*指向拥有该块的缓冲区页的描述符的指针 */

 

      sector_t b_blocknr;               /*与块设备相关的块号(起始逻辑块号) */

      size_t b_size;                    /*块大小 */

      char *b_data;                     /*块在缓冲区页内的位置 */

 

      struct block_device*b_bdev;      /*指向块设备描述符的指针 */

      bh_end_io_t*b_end_io;            /* I/O完成方法 */

       void *b_private;                 /*指向I/O完成方法数据的指针 */

      struct list_headb_assoc_buffers; /*为与某个索引节点相关的间接块的链表提供的指针 */

      atomic_t b_count;                 /*块使用计数器 */

};

在使用缓冲区之前,内核首先必须创建一个 buffer_head结构实例,这是一个频繁出现的任务,内核使用slab分配器来实现。注意,在一个缓冲区页内的所有块缓冲区的大小必须相同。在80x86体系结构上,块大小至少512,因此一个缓冲区页可以包括1~8个缓冲区。

 

那么缓冲区和页是如何关联起来的呢?下图显示了一个缓冲区页,其中包含4个块缓冲区和对应的缓冲区首部。

 


 

图中可以看出,pageprivate成员指向第一个缓冲头,各个缓冲头通过b_this_page连接为一个环形链表。这样的结构使得内核从page结构开始,就能轻易扫描与页关联的所有缓冲头。具体实现在函数create_empty_buffers()中。这个函数很简单,首先建立一个环形链表,根据内存页的状态设置缓冲区状态,即设置BH_DirtyBH_Uptodate,如果有的话。最后将pageprivate成员指向第一个缓冲头,并设置页标志的PG_Private,声明该页与缓冲区关联。

 

只要内核必须单独地访问一个块,就要涉及存放块缓冲区的缓冲区页,并检查相应的缓冲区首部。下面是内核创建缓冲区页的两种普通情况:

1)当读或写的文件页在磁盘块中不相邻时。发生这种情况是因为文件系统为文件分配了非连续的块,或因为文件有“洞”。

2)当访问一个单独的磁盘块时(例如,当读超级块或索引节点块时)。

下面讲一个函数:block_read_full_page()。它的工作是:以一次读一块的方式将磁盘数据读取到缓冲区页,分为3步,如下图所示:


 

1)建立缓冲区并检查其状态,找出真正需要读入的记录块缓冲区,将其buffer_head放到指针数组arr[]中

2)锁定arr[]中的缓冲区,防止其他内核线程在下一步进行干扰;

3)数据传输到缓冲区。

可以看出,block_read_full_page()的优点在于,它只需读取页中并非最新的那些部分。如果能确定整页都不是最新的,最好调用mpage_readpage(),避免缓冲区的多余开销。

 

虽然本文一开始提到,不依靠于页缓存的缓冲区已经不用了,可是《深入Linux内核架构》一书中提到,我们在提一提吧~这种缓存叫做LRUleast recentlyused)块缓存,即最近最少使用。根据名字不难知道这是一种非常简单的组织方式。

独立的块缓存和用作页缓存附加功能的块缓存使用的数据结构时相同的,每个缓冲区都有一个缓冲头。内核中所有独立缓冲区的缓冲头保存在一个数组bhs[]中。当内核请求查找一个独立的缓冲区时,首先扫描数组中所有的缓存项,若命中则使用该缓存项数据,并把它放置在LRU列表第一个位置上,其他元素顺延改变位置;否则向块设备请求数据并将数据保存在位于LRU列表首位的新建的一个独立缓冲区中,其他元素顺延改变位置,最后一个元素退出缓存。

原创粉丝点击