首页 百科知识 块高速缓冲

块高速缓冲

时间:2022-10-09 百科知识 版权反馈
【摘要】:Linux支持的文件系统大都以块的形式组织文件,为了减少对物理块设备的访问,所有Linux文件系统使用一个通用buffer cache来缓冲来自底层设备的数据,以便加速对包含此文件系统的物理设备的存取。这个buffer cache与文件系统无关,并被集成到Linux内核分配与读写数据缓存的机制中。系统中全部的块缓冲,包括那些没使用过的新缓冲都保存在此buffer cache中。

6.5.3 块高速缓冲

Linux支持的文件系统大都以块的形式组织文件,为了减少对物理块设备的访问,所有Linux文件系统使用一个通用buffer cache来缓冲来自底层设备的数据,以便加速对包含此文件系统的物理设备的存取。这个buffer cache与文件系统无关,并被集成到Linux内核分配与读写数据缓存的机制中。

当文件以块的形式调入内存后,可以使用块缓冲区对它们进行管理。每个块缓冲区中存在一个缓冲区首部(用数据结构buffer_head表示)和存储数据的缓冲区空间。缓冲区首部不与数据区域相连,数据区域独立存储,两者之间由一个指针*b_data链接。

所有数据块的读/写请求以buffer_head数据结构的形式传递给设备驱动程序。它们提供了设备驱动所需的所有信息:表示设备的设备标志符以及请求的块号。所有块设备都被看成相同块大小的线性块集合。为了加速对物理块设备的访问,Linux使用了一个块buffer cache。系统中全部的块缓冲,包括那些没使用过的新缓冲都保存在此buffer cache中。这个Cache被多个物理块设备共享;任何时刻此Cache中都有许多属于不同系统块设备且状态不同的块缓冲。如果有效数据可以从buffer cache中找到则将节省大量访问物理设备的时间。任何对块设备读写的块缓冲都被放入此Cache中。随时间的变化有些块缓冲可能将会被此Cache中删除以为更需要它的缓冲腾出空间,如果它被频繁使用则可以一直保存在此Cache中。

(1)块高速缓冲区结构

块高速缓冲时文件系统和设备进行数据传输的桥梁,它同时涉及到不同的物理设备和逻辑文件系统,是各种设备驱动程序数据传输的接口,因此是一个相对复杂的数据结构。

每一个可以使用的缓冲区由两部分组成:

buffer_head:用于控制的缓冲头部;

buffer:存放数据的缓冲数据区。

缓冲头部大致包括三类信息:

用于描述缓冲区内容的信息,包括所在设备号,起始物理块号,包含在缓冲区中的字节数。

描述缓冲区状态和访问计数的信息。

用于缓冲区管理的信息。

缓冲头部的定义如下:

struct buffer_head {

 /* First cache line: */

 unsigned long   b_blocknr;  /* block number            */

 kdev_t      b_dev;    /* device (B_FREE = free)     */

 kdev_t       b_rdev;   /* Real device             */

 unsigned long   b_rsector;  /* Real buffer location on disk  */

 struct buffer_head *b_next;   /* Hash queue list          */

 struct buffer_head *b_this_page; /* circular list of buffers in one page*/

 /* Second cache line: */

 unsigned long   b_state;   /* buffer state bitmap (above)  */

 struct buffer_head *b_next_free;

 unsigned int    b_count;   /* users using this block */

 unsigned long   b_size;   /* block size             */

 /* Non-performance-critical data follows. */

 char    *b_data;   /* pointer to data block        */

 unsigned int   b_list;   /* List that this buffer appears */

 unsigned long   b_flushtime; /* Time when this (dirty) buffer should be written */

 unsigned long   b_lru_time;  /* Time when this buffer was last used. */

 struct wait_queue *b_wait;

 struct buffer_head *b_prev;   /* doubly linked hash list    */

 struct buffer_head *b_prev_free; /* doubly linked list of buffers */

 struct buffer_head *b_reqnext;  /* request queue              */

};

为了支持各种不同类型的逻辑文件系统,Linux系统缓冲数据区的大小不是固定的,按照不同的长度分为7种类型。在同一时刻,缓冲区中可能包含不同物理设备的数据块。

在Linux的块高速缓冲中,缓冲数据区和缓冲头部并不是完全一一对应的,整个块高速缓冲区中有四类以缓冲头部为节点的链表,其中有两类不包含数据区,并不能进行实际的数据缓冲,主要涉及缓冲头部的管理,包括未使用链表unused_list和重用链表reused_list,都采用单向链表存放;另外两类是真正的缓冲区,每一个链表的节点都是由一一对应的缓冲头部和缓冲数据区组成,分别是空闲链表free_list和正在使用链表lru_list,都使用双向链表保存。

Linux的缓冲系统由一个hash表和四个struct buffer_head链表构成,它们的定义如下:

static struct buffer_head **hash_table /*散列表*/

static struct buffer_head * lru_list[NR_LIST] = {NULL, };/*最近最少使用buffer链表*/

static struct buffer_head * free_list[NR_SIZES] = {NULL, };/*空闲buffer链表*/

static struct buffer_head * unused_list = NULL; /*未使用buffer链表*/

static struct buffer_head * reuse_list = NULL; /*重用buffer链表*/

空闲链表free_list中保存由内存页面初次创建的缓冲区和从正在使用链表中回收的空缓冲区。系统中一共有7个这样的链表,分别对应于不同长度的缓冲区类型,他们共同组成一个链表数组。

正在使用链表lru_list存放当前正在使用的缓冲区,它们可以向free_list申请得到。写回设备操作的缓冲区数组共有4个指针元素,分别指向一个缓冲区链表:

lru_list[0],BUF_CLEAN链表,数据已经写出、干净的缓冲区;

lru_list[1],BUF_LOCKED链表,正在进行写设备操作的缓冲区;

lru_list[2],BUF_LOCKED1链表,用于超级块或inode的缓冲区;

lru_list[3],BUF_DIRTY链表,“脏”缓冲区,写入新数据,等待写出的缓冲区。

reuse_list接收从I/O设备中断服务程序返还的缓冲区对应的buffer_head。这个缓冲区是为了数据的传输而申请的。

unused_list保存刚从内存管理模块申请到的尚未使用的buffer_head,或由recover_reusable_buffer_heads()函数从reuse_list恢复buffer_head。free_list回收来自lru_list或unused_list的buffer_head。

为了提高查找效率,Linux还同时使用一个散列表(hash table)来组织所有的正在使用的链表。表的节点类型是缓冲头部buffer_head类型,散列索引值通过缓冲区所对应物理块设备号及其块号来产生,具有相同索引数值的正在使用链表的节点组成新的双向链。

(2)块高速缓冲区的分配和释放

分配块高速缓冲区的工作由getblk函数完成。首先根据块对应的设备号和块号在散列表中搜索正在使用的链表中是否包含这个数据块,如果包含,就得到需要的缓冲区,可以进行数据操作,否则,就需要重新分配一个空闲缓冲区。

分配空闲缓冲区时,根据所操作数据块的大小在相应的空闲缓冲区链表中查找,如果找到一个合适的空闲缓冲区,从链表中摘下该缓冲区,并初始化该缓冲区的控制数据,然后挂到相应的散列表和干净缓冲区链表中,得到可以使用的缓冲区。

如果空闲缓冲区中没有合适的缓冲区,系统就通过两种途径来补充空闲缓冲区的数量:

一是申请空闲的内存页面,建立所需长度的新缓冲区并挂到相应的空闲链表中。

二是唤醒进程bdflush把“脏”缓冲区写出到设备,以得到空闲缓冲区。

在完成这些操作的过程中,可能已经有别的进程曾经对这里需要访问的数据块进行过访问并在正在使用链表中建立了节点,因此再进行一次散列表的搜索,如果还没有找到,就从空闲缓冲区中获取。

缓冲区的释放由tr_to_free_buffers函数完成。

(3)块高速缓冲区数据维护

维护块高速缓冲区,写到设备上的工作由两个内核线程bdflush和update来完成。bdflush的进程号为2,进程名称为kflushd。update的进程号为3,进程名称为kupdate。

bdflush是对过多的dirty缓冲系统提供动态响应的简单核心后台进程;这些缓冲块中包含必须被写入到硬盘上的数据。它在系统启动时作为一个核心线程运行,其名字叫"kflushd"。你可以使用ps命令看到此系统进程。通常情况下此进程一直在睡眠直到系统中的dirty缓冲数目增大到一定数目。当分配与丢弃缓冲时,系统中dirty缓冲的数目将做一个统计。如果其数目超过某个数值则唤醒bdflush进程。缺省的阀值为60%,但是如果系统急需缓冲则任何时刻都可能唤醒bdflush。使用update命令可以看到和改变这个数值。

# update –d

bdflush version 1.4

0:  60 Max fraction of LRU list to examine for dirty blocks

1:  500 Max number of dirty blocks to write each time bdflush activated

2:  64 Num of clean buffers to be loaded onto free list by refill_freelist

3:  256 Dirty block threshold for activating bdflush in refill_freelist

4:  15 Percentage of cache to scan for free clusters

5: 3000 Time for data buffers to age before flushing

6:  500 Time for non-data (dir, bitmap, etc) buffers to age before flushing

7: 1884 Time buffer cache load average constant

8:   2 LAV ratio (used to determine threshold for buffer fratricide).

但有数据写入缓冲使之变成dirty时,所有的dirty缓冲被连接到一个BUF_DIRTY LRU链表中,bdflush会将适当数目的缓冲块写到磁盘上。这个数值的缺省值为500。

update命令不仅仅是一个命令;它还是一个后台进程。当作为超级用户运行时(在系统初始化时),它将周期性调用系统服务例程,将老的dirty缓冲区内容冲刷到磁盘上去。它所完成的这个工作与bdflush类似。当一个dirty缓冲完成此操作后,它将把本应写入到各自磁盘上的时间标记到buffer_head结构中。update每次运行时它将在系统中的所有dirty缓冲中查找那些冲刷时间已超过一定期限的缓冲区,这些过期缓冲都被写入到磁盘。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈