buffer_head的理解

在上一篇博客介绍address_space中,我们有提到,内存中一个page所包含的磁盘块在物理上不一定是相邻的。那么page中不同的磁盘块怎么管理呢?这里就涉及到了buffer_head结构。(实际上,我们的epsiode中设定了block大小和page 大小是一样的,都是4k,但buffer_head仍然大量存在,例如sb中的imap zmap,sbh,以及管理blockid的i_zone[10],我们下一步要做的i_index[]可能也得采用它)

把块存放在页高速缓存page cache中就会涉及到块缓存区和缓冲区首部。

每个buffer_head管理的单元是内存页page中对应的一个物理块(对于block大小为1k的情况下,一个page对应磁盘上的4个block,而每个buffer_head对应1个磁盘block),也就是说buffer_head管理单元是 “页”的一个子集。

下面以页的大小为4k,block大小为1k来描述这个问题。在这种情况下,一个页中就会有四个缓冲区对应着四个buffer_head结构管理。页描述符page 中private指针指向其中一个缓冲区首部,缓冲区首部内部通过链表结构(b_this_page)链接该页对应的四个缓冲区。缓冲区首部(buffer_head)的b_page指针指向对应的page结构,b_data指向数据在对应的页中的位置。

如下图:

buffer_head的理解_第1张图片

 

下面部分结合代码内容分析如何将buffer_head所管理的缓冲区转化成下发到磁盘的io请求。

首先submit_bh()函数,其作用:根据缓冲区首部的内容创建一个bio,在该函数中通过buffer_head传进来的信息对bio的信息赋值。如下

1

2

3

4

5

bio->bi_sector = bh->b_blocknr * (bh->b_size >> 9); //扇区号

bio->bi_bdev = bh->b_bdev; //设备,如分区

bio->bi_io_vec[0].bv_page = bh->b_page; //对应页

bio->bi_io_vec[0].bv_len = bh->b_size;  //块大小

bio->bi_io_vec[0].bv_offset = bh_offset(bh); //buffer_head指向的缓存区相对于其所在页的偏移

然后submit_hb()调用submit_bio(),把rw数据传输方向赋值。

在submit_bio()中调用generic_make_request()函数。通过generic_make_request()函数把bio递交到通用块层。有关generic_make_request的函数解析,参照:

http://blog.chinaunix.net/uid-9988622-id-1995526.html

http://www.powerlinuxchina.net/club/viewthread.php?action=printable&tid=1239

generic_make_request()函数式递交一个bio的开始。每个进程的task_struct中,都包含两个变量: struct bio *bio_list,**bio_tail,该函数就是通过维护这两个变量来维护待添加的bio链表。generic_make_request函数调用__generic_make_request函数完成。而在__generic_make_request会调用make_request_fn,make_request_fn可能自行实现时递归调用generic_make_request,有了栈的深度有限,英雌通过genenric的设计,是的递归调用深度不超过1,具体是:一旦开始把当前的bio链表下发,则新到的bio不能加入正在下发的链表中。

generic_make_request函数调用__generic_make_request.

__generic_make_request做的事情很简单,先通过blk_partition_remap检查设备是否为磁盘分区,然后把相对于分区的起始扇区号转化为相对于整个磁盘的扇区号,然后调用q->make_request_fn()到达IO调度层,而make_request_fn主要是通过__make_request()函数实现的

 

buffer_head的理解_第2张图片

 

你可能感兴趣的:(文件系统,linux)