buffer cache分析
1. buffer cache概述
Buffer cache是一个双向循环链表,用于缓存磁盘上的数据以提高性能。使用bread从磁盘上读取数据,使用bwrite将修改的数据写回磁盘,使用bread或者bwrite之后需要调用brelease来释放buffer cache结点。
buffer cache通过一个bcache全局结构体变量来描述:
其中spinlock用来保护对buffer cache链表的访问,NBUF的值默认是30,其中struct buf是用来描述每一个buffer cache的:
2. binit函数
binit函数主要是将31个buffer cache结构体初始化形成双向循环链表:
3. bread函数
bread函数返回一个磁盘扇区对应的buffer cache结构体,如果磁盘的内容已经被缓存,则直接返回该结构体,否则从磁盘上读取数据到一个空闲的buffer cache中,然后再返回此结构体。
bread通过调用bget获取一个可用的buffer cache,bget内容如下:
访问buffer cache链表之前需要通过bcache.lock锁住链表,bget通过两次链表遍历来寻找可用的buffer cache。
第一次遍历链表来寻找要申请的磁盘区号是否已经缓存在链表中,判断方法是检查每个buffer cache结构体中的dev和blockno是否和传入的一样,如果一样,表示此磁盘块内容已经被缓存。那么接下来就将此buffer cache结构体中的引用计数加一,然后释放链表访问锁,接着获取此buffer cache的睡眠锁,表示此buffer cache有进程在使用。注意第一次遍历是从head->next开始的,因为head->next的buffer cache是最近一次cache的缓存,其最可能就是要获取的buffer cache。
如果要读的磁盘内容没有被缓存,那么就需要进行第二次链表遍历,以寻找一个可用的buffer cache结构体。这次的遍历顺序从head->prev开始,因为head->prev的buffer cache是最近不被使用的buffer cache,其最有可能是空闲的。如果buffer cache的引用计数是0并且此buffer不是脏的,那么就表示此buffer cache是空闲可用的,接着讲dev和blockno记录在此buffer cache中,同时清除flag标志成员,引用计数变为1,因为这是第一次引用,同时释放链表访问锁,接着获取此buffer cache的睡眠锁,表示此buffer cache有进程在使用。
b->flags的B_DIRTY位是在磁盘驱动的中断函数中被清除的。也就是说如果一个Buffer cache是空闲的,那么这块buffer cache的数据一定是和磁盘上一样的。
从以上分析可以看出,bget正常返回时返回的是一个锁住的buffer cache结构体。
回到bread看看:
获取到buffer cache结构体后,接着检查此buffer中数据是否有效,如果无效,那么就要从磁盘上读取数据了,通过iderw从磁盘硬件上读取数据。
从上面分析可以看出,bread返回的是锁住的带有有效数据的buffer cache结构体。
4. bwrite函数
bwrite很简单,就是将数据写回磁盘,但是有个前提条件就是,当调用bwrite时,此buffer cache一定要是锁住的状态。
首先检查了本进程是否拥有buffer cache的睡眠锁,如果没有直接报错。接着将buffer cache的标志位设置为脏,表示此buffer cache的数据被改写,最后调用iderw将buffer cache中的数据写回磁盘。
5. brelse函数
brelse用于释放一个锁住的buffer cache:
首先检查了本进程是否拥有buffer cache的睡眠锁,如果没有直接报错。然后将此buffer cache解锁,这时此buffer cache就可以被其他进程使用了。
接着将buffer cache的引用计数减一,如果引用计数为0,表示没有进程在使用这块Buffer cache了,所以需要将此buffer cache归还到链表中。归还方式是将此Buffer cahce结构体放入head->next,所以head->next后的Buffer肯定表示最近被使用的buffer cache。
评论