[数据缓冲区高速缓存]
内核通过保存一个称为数据缓冲区高速缓存的内部数据缓冲区池来试图减小对磁盘的存取频率。高速缓冲含有最近被使用过的磁盘块的数据
在Linux内核0.11中,它默认最多支持16M的物理内存。对系统内存的分配情况如下:Linux内核占用物理内存最前段的一部分。随后是高速缓冲区,它的最高内存地址为4MB。高速缓冲区被显示内存和ROM BIOS分成两段。剩余的内存部分称为主内存区,当然若系统中还存在RAM虚拟盘,则主内存区前段还要扣除虚拟盘所占的内存空间。示意图如下。
[高速缓冲区的工作原理为]
当内核从磁盘中读取数据时,内核试图先从高速缓冲中读。如果数据已经在该高速缓冲中,则内核可以不必从磁盘上读。如果数据不在该高速缓冲中,则内核从磁盘上读数据,并将其缓冲起来。
写操作与之类似,要往磁盘上写的数据也被暂存于高速缓冲中,以便如果内核随后又试图读它时,它能在高速缓冲中。内核也通过判定是否数据必须真的需要存储到磁盘上,或数据是否是将要很快被重写的暂时性数据,来减少磁盘写操作的频率。
[高速缓存的数据结构]
整个高速缓存由很多缓存块组成,在Linux 0.11版本中,整个高速缓存由307块高速缓存块组成。每个缓存块主要由两部分组成:1、被缓存的数据部分;2、标识该缓存块信息的部分。将标识该缓存块信息的部分称为缓存头,buffer_head。在buffer_head中由指针指向被缓存的数据buffer_data。每个高速缓存块的组成示意图如下
buffer_data指向的缓存数据大小是与文件系统上一个逻辑块的大小相对应的。也就是说,在对数据进行缓存的时候,是以逻辑块为单位的。Linux 0.11中高速缓存相关的数据结构如下所示(代码来自于fs.h)
struct buffer_head {
char * b_data; //指向缓存的数据块的指针
unsigned long b_blocknr; //缓存的逻辑块号
unsigned short b_dev; //缓存的设备号
unsigned char b_uptodate; //缓存中的数据是否是最新的
unsigned char b_dirt; //缓存中数据是否为脏数据
unsigned char b_count; //这个缓存块被引用的次数
unsigned char b_lock; //b_lock表示这个缓存块是否被加锁
struct task_struct * b_wait; //等待在这个缓存块上的进程
struct buffer_head * b_prev; //指向缓存中下一个缓存块
struct buffer_head * b_next; //指向缓存中上一个缓存块
struct buffer_head * b_prev_free; //缓存块空闲链表中指向下一个缓存块
struct buffer_head * b_next_free; //缓存块空闲链表中指向上一个缓存块
};
b_dev和b_blocknr的意思很容易理解。Linux支持的块设备,每个设备都有一个设备号。每个设备号在逻辑上可以看成是连续的许多大小相同的块组成,每个块都有一个编号。由于一个缓存块对应一个逻辑块数据,所以这里b_dev和b_blocknr表示此缓存块所缓存的数据说在的设备号和逻辑块号。
b_uptodate和b_dirt的含义不是那么明显,要理解它们之间的区别。
b_dirt是一个标志位,用来表示缓存块中的数据有没有被修改过,这个修改是针对它所对应的物理设备上的数据而言的,即将设备数据缓存到缓存区之后,数据在缓存中有没有被修改,从而造成了缓存中的数据和设备上数据的不一致。
b_uptodate也是一个标志位,用来表示缓存块中的数据是不是最新的。这个更新标志也是对物理上的设备而言的,即缓存中的数据是不是最新的,或者说它标志着缓存中的数据是不是有效的,可能设备中的数据被修改,但缓存中数据没有被相应的同步时,这个标志就表明,缓存中的数据与设备中数据不一致。
b_count表示这个缓存块目前被引用的次数,b_count为0时表示这个缓存块目前是空闲的。
管理告诉缓存的的四个指针:b_prev、b_next、b_prev_free和b_next_free。要理解这四个指针的作用,必须要先说明下整个高速缓存的结构。
在前面说过,高速缓存由许多的缓存块组成,这些缓存块之间使用b_prev_free和b_next_free构成一个循环的双向链表,由一个内核变量free_list作为整个双向链表的头。free_list指向的循环的双向链表中,包含了所有的缓存块,空闲的和非空闲的都在这个链表中。只不过当一个缓存块由空闲变为非空闲之后,会将其移动到链表的最后面去。
对于空闲的缓存块,b_prev和b_next指针都是空。对于非空闲的缓存块,它同时存在另一个结构中。这个结构是有数据缓存的部分。这是一个hash散列数组,数组大小是NR_HASH(307)。散列值的计算使用的是(b_dev^b_blocknr)%NR_HASH,使用散列值作为索引存储。但出现散列值冲突时,使用双向链表解决冲突,这就是b_prev和b_next的作用了。
整个结构可以用如下示意图进行说明
图中的黑色箭头部分代表b_prev和b_next,蓝色箭头部分代表b_prev_free和b_next_free