为了提高文件系统性能,内核的每次读写请求并不是都直接通过驱动程序访问磁盘进行I/O,而是要经过高速缓冲区。如下图所示:
每个高速缓冲块对应一个磁盘块,都为1024B。为管理这些高速缓冲块,内核使用buffer_head数据结构。如下:
struct buffer_head {
char * b_data; /* pointer to data block (1024 bytes) */高速缓冲块指针
unsigned long b_blocknr; /* block number */块号
unsigned short b_dev; /* device (0 = free) */数据源的设备号
unsigned char b_uptodate; //更新标志:表示数据是否已更新
unsigned char b_dirt; /* 0-clean,1-dirty */修改标志:0未修改,1已修改
unsigned char b_count; /* users using this block */使用的用户数
unsigned char b_lock; /* 0 - ok, 1 -locked */ 缓冲区是否被锁定
struct task_struct * b_wait; //指向等待该缓冲区解锁的任务
struct buffer_head * b_prev; //hash 队列上前一块(用于缓冲区管理)
struct buffer_head * b_next; //hash 队列上后一块
struct buffer_head * b_prev_free; //空闲表上前一块
struct buffer_head * b_next_free; //空闲表上后一块
};
所有缓冲块头组成双向循环链表:
实际上这个双向链表是一个最近最少使用LRU链表,free_list是该链表最空闲的缓冲块处的头指针。当内核需要缓冲块,而缓冲区中的缓冲块都已被使用时,则需要替换其中的缓冲块,则从free_list开始寻找缓冲块。
为了快速查找需要的磁盘块数据是否已经在缓冲区中存在,即缓冲区中是否有需要的缓冲块。使用hash队列,以设备号和块号作hash,将buffer_head置于各hash队列中。搜索时则直接从对应的hash队列中进行。其结构如图:
因此,内核取得缓冲块的算法如下:
如果指定的缓冲块存在于hash表中,则说明已经得到可用的缓冲块,于是直接返回。
否则就需要在链表中从free_list头指针处开始搜索,即从最近最少使用的缓冲块开始。
对应函数getblk()的流程如下:
获得缓冲块后,根据缓冲块中数据是否有效决定是否从磁盘读取数据。
整个获得缓冲区函数bread()流程如下:
详见linux内核完全剖析
阅读(4097) | 评论(0) | 转发(0) |