块缓存实现
块患处不仅仅用作页面缓存的附加功能,对以块而不是页面进行处理的对象来说,块缓存是一个独立的缓存。
数据结构
块缓冲区头
structbuffer_head {
unsignedlongb_state;/* buffer state bitmap (see above) */
structbuffer_head *b_this_page;/* circular list of page's buffers */
structpage *b_page;/* the page this bh is mapped to */
sector_t b_blocknr;/* start block number */
size_tb_size;/* size of mapping */
char*b_data;/* pointer to data within the page */
structblock_device *b_bdev;
bh_end_io_t *b_end_io;/* I/O completion */
void*b_private;/* reserved for b_end_io */
structlist_head b_assoc_buffers;/* associated with another mapping */
structaddress_space *b_assoc_map;/* mapping this buffer is
associated with */
atomic_t b_count;/* users using this buffer_head */
};
操作
内核必须提供一组操作,使得其余代码能够轻松有效地利用缓冲区的功能。切记:这些机制对内存中实际缓存的数据没有贡献。
在使用缓冲区之前,内核首先必须创建一个buffer_head结构实例,而其余的函数则对该结构进行操作。因为创建新缓冲头是一个频繁重现的任务,他应该尽快执行。这是一种很经典的情形,可使用slab缓存解决。
切记:内核源代码确实提供了一些函数,可用作前端,来创建和销毁缓冲头。alloc_buffer_head生成一个新的缓冲头,而free_buffer_head销毁一个显存的缓冲头。
/*分配buffer_head*/
structbuffer_head *alloc_buffer_head(gfp_t gfp_flags)
{
/*从slab中分配空间*/
structbuffer_head *ret = kmem_cache_alloc(bh_cachep, gfp_flags);
if(ret) {
/*初始化*/
INIT_LIST_HEAD(&ret->b_assoc_buffers);
get_cpu_var(bh_accounting).nr++;
recalc_bh_state();
put_cpu_var(bh_accounting);
}
returnret;
}
页缓存和块缓存的交互
一页划分为几个数据单元,但缓冲头保存在独立的内存区中,与实际数据无关。与缓冲区的交互没有改变的页的内容,缓冲区只不过为页的数据提供了一个新的视图。
为支持页与缓冲区的交互,需要使用struct page的private成员。其类型为unsigned long,可用作指向虚拟地址空间中任何位置的指针。
Private成员还可以用作其他用途,根据页的具体用途,可能与缓冲头完全无关。但其主要的用途是关联缓冲区和页。这样的话,private指向将页划分为更小单位的第一个缓冲头。各个缓冲头通过b_this_page链接为一个环形链表。在该链表中每个缓冲头的b_this_page成员指向下一个缓冲头,而最后一个缓冲头的b_this_page成员指向第一个缓冲头。这使得内核从page结构开始,可以轻易地扫描与页关联的所有buffer_head实例。
内核提供cteate_empty_buffers函数关联page和buffer_head结构之间的关联:
/*
* We attach and possibly dirty the buffers atomically wrt
* __set_page_dirty_buffers() via private_lock. try_to_free_buffers
* is already excluded via the page lock.
*/
voidcreate_empty_buffers(structpage *page,
unsignedlongblocksize, unsignedlongb_state)
{
structbuffer_head *bh, *head, *tail;
head = alloc_page_buffers(page, blocksize, 1);
bh = head;
/*遍历所有缓冲头,设置其状态,并建立一个环形链表*/
do{
bh->b_state |= b_state;
tail = bh;
bh = bh->b_this_page;
}while(bh);
tail->b_this_page = head;
spin_lock(&page->mapping->private_lock);
/*缓冲区的状态依赖于内存页面中数据的状态*/
if(PageUptodate(page) || PageDirty(page)) {
bh = head;
do{/*设置相关标志*/
if(PageDirty(page))
set_buffer_dirty(bh);
if(PageUptodate(page))
set_buffer_uptodate(bh);
bh = bh->b_this_page;
}while(bh != head);
}
/*将缓冲区关联到页面*/
attach_page_buffers(page, head);
spin_unlock(&page->mapping->private_lock);
}
staticinlinevoidattach_page_buffers(structpage *page,
structbuffer_head *head)
{
page_cache_get(page);/*递增引用计数*/
/*设置PG_private标志,通知内核其他部分,page实例的private成员正在使用中*/
SetPagePrivate(page);
/*将页的private成员设置为一个指向环形链表中第一个缓冲头的指针*/
set_page_private(page, (unsignedlong)head);
}