Linux高速缓存详解

在Linux高速缓存概述中介绍了Linux 0.11中的高速缓存的基础结构,这一部分将详细分析Linux高速缓存部分的相关函数。


[数据结构]
这里介绍下另外和高速缓存相关的数据结构,高速缓存散列表、空闲链表指针free_list以及等待在缓存块上的指针buffer_wait。它们定义分别如下:(代码来源buffer.c)
struct buffer_head * hash_table[NR_HASH];
static struct buffer_head * free_list;
static struct task_struct * buffer_wait = NULL;
struct buffer_head * start_buffer = (struct buffer_head *) &end;
int NR_BUFFERS = 0;


hash_table就是之前介绍过的缓存散列表,散列函数定义如下
#define _hashfn(dev,block) (((unsigned )(dev^block))%NR_HASH)
#define hash(dev,block) hash_table[_hashfn(dev,block)]
使用两个宏,_hashfn(dev,block)计算相应的设备号和逻辑块号在散列表上的索引,使用hash(dev,block),获取计算的索引值在hash_table上的项,也就是指向双向链表的指针


free_list指向所有缓存块构成的双向链表的指针,可以将其看成是整个链表的头指针


buffer_wait是一个指向进程的指针,当进程试图获取一个缓存块时。如果所有的缓存块都不是空闲的,就会让buffer_wait指向这个进程,并且让这个进程进入不可中断的睡眠状态。当有任何进程释放一个缓存块时,就会唤醒buffer_wait指向的进程。(个人对此有个问题,当所有的缓存块都非空闲时,如果有多个进程试图获取缓存块,这几个进程都会进入不可中断的睡眠状态。但buffer_wait只是指向最后调用的那个进程,因此,当缓存块被释放,只有最后那个进程被唤醒,其它进程会一直处于不可中断的睡眠状态?)


start_buffer表示缓冲区开始地址,end是由连接程序ld生成的表明程序末端的变量(来自《Linux内核完全注释》对end的解释)


NR_BUFFERS表示缓冲块的个数,NR_BUFFERS其实是一个宏,在fs.h中定义,对应变量nr_buffers。
[缓冲区的初始化]
缓冲区的初始化由buffer_init完成。缓冲区的初始化过程是这样的,从缓冲区的高地址部分开始,划分出一个个大小为1KB(BLOCK_SIZE)部分作为数据缓冲部分,也就是在Linux高速缓存概述中介绍的buffer_data部分。从缓冲区的低地址部分开始,建立对应每个buffer_data部分的缓冲块头部分,也就是buffer_head部分。初始化之后的缓冲区的示意图如下

Linux高速缓存详解


初始化代码如下所示
/*
buffer_end表示缓冲区的结束地址
*/
void buffer_init(long buffer_end)
{
                struct buffer_head * h = start_buffer;
                void * b;
                int i;
                /*
                由于内存地址的640K~1M部分被用于显存和BIOS使用,所以这里需要对缓存的结束地址做调整
                */
                if (buffer_end == 1<<20)  //2^20=1M
                                b = ( void *) (640*1024); //640K
                else
                                b = ( void *) buffer_end;
      /*
          为了保证有足够的空间来存储缓冲块的头部和缓冲块的数据缓冲部分,需要满足b-BLOCK_SIZE>=h+1
    */
                while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) {
                        //设置缓冲块头部的初始值,并让data指向数据缓冲部分
                                h->b_dev = 0; 
                                h->b_dirt = 0; 
                                h->b_count = 0;
                                h->b_lock = 0;
                                h->b_uptodate = 0;
                                h->b_wait = NULL;
                        //空闲的缓冲块的b_next和b_prev都为NULL
                                h->b_next = NULL; 
                                h->b_prev = NULL;
                                h->b_data = ( char *) b;  //让data指向数据缓冲部分
                                h->b_prev_free = h-1;
                                h->b_next_free = h+1;
                                h++;
                                NR_BUFFERS++;
                        /*
                              看是否到了显存和BIOS使用的内存区域,如果是,就要调整高地址
                        */
                                if (b == (void *) 0x100000)//16^5=2^20=1M
                                                b = (void *) 0xA0000; //0xA0000=640K
                }
                h--;
                free_list = start_buffer; //让free_list指向链表头
                free_list->b_prev_free = h;  //构造循环结构
                h->b_next_free = free_list;
          //初始化缓冲区的散列表,所有项都设为空
                for (i=0;i<NR_HASH;i++)
                                hash_table[i]=NULL;
}

相关推荐