赞
踩
在上一篇博客介绍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所管理的缓冲区转化成下发到磁盘的io请求。
首先submit_bh()函数,其作用:根据缓冲区首部的内容创建一个bio,在该函数中通过buffer_head传进来的信息对bio的信息赋值。如下
1 2 3 4 5 |
|
然后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()函数实现的
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。