buffer_head和bio

一、page和buffer_head的关系

 

1、页中的块在磁盘上连续

如果page中的块在磁盘上连续,那么page的PG_private不会被置位,private字段也不会指向buffer_head的链表。

 

但是page还是得用到buffer_head结构,因为它需要通过get_block()函数来获得磁盘上的逻辑块号。

虽然ext2_getblock()函数的代码我暂时还没有看,但是通过do_mpage_readpage()函数代码的阅读,可以对get_block()系列函数的功能进行如下猜想:

typedef int (get_block_t)(struct inode *inode, sector_t iblock,

struct buffer_head *bh_result, int create);

这类函数会得到在文件中块号iblock在磁盘上的逻辑块号,然后赋给bh中的b_blocknr字段。在调用get_block()函数前,bh中的b_size被赋为期望的连续的块数的总大小,返回前,get_block()函数被设置为以iblock块为第一块(什么意思?),且在磁盘上连续的实际的块数(如果实际连续的比期望的小)。

 

在do_mpage_readpage()函数中,得到了块在磁盘上的逻辑块号后,buffer_head结构就没有什么用了,将其中的b_blocknr赋给了blocks数组后,生成bio的函数mapge_alloc()使用blocks[0]就行了。

Blocks[0]是否表示磁盘上连续的实际的块数的总的大小(以位为单位)?

 

2、页中的块在磁盘上不连续

页一开始和buffer_head是没有关系的,但是通过get_block()发现页中的块在磁盘上不连续等现象后,就需要调用create_empty_buffers()函数来为page创建buffer_head链表了。create_empty_buffers的结构很简单,它先调用alloc_page_buffers()来为page创建一个buffer_head的链表,之后为链表中每个buffer_head的b_state赋值,并顺便将该链表构造成循环链表,然后看情况设置buffer_head的BH_dirty和BH_uptodate标志,最后调用attach_page_buffers()来将page的PG_private置位。

链表建成后,page和buffer_head的关系就如下图所示了:

Struct buffer_head *b_this_page 是否指向下一个Buffer head?确定

 

二、buffer_head和bio的关系

个人认为,buffer_head和bio关系在submit_bh()函数中可以充分体现:(也就是说只有在page中的块不连续时,buffer_headbio才建立关系?)

 

       bio = bio_alloc(GFP_NOIO, 1);

 

       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; //应该不一定对应一个block的大小

       bio->bi_io_vec[0].bv_offset = bh_offset(bh);

 

       bio->bi_vcnt = 1;

       bio->bi_idx = 0;

       bio->bi_size = bh->b_size;

 

上述代码已经把buffer_head和bio关系说的差不多了,就不多说了。

稍微注意一点的话,可以发现io_vec中的bv_page指向了buffer_head中的b_page,即bv_page指向了也描述符,而bv_offset则是在页中的偏移,为len则为要传输的数据的(在这里就是块的大小)长度。

 

 

三、page和bio的关系

page和bio的关系在上面一段中稍微说了一下,即io_vec中的bv_page字段会指向page。

将一个整页加到bio中,可以看看_add_page函数中的如下几行(do_mpage_readpage()函数调用bio_add_page()时,offset参数是0):

 

       bvec = &bio->bi_io_vec[bio->bi_vcnt];

       bvec->bv_page = page;

       bvec->bv_len = len;

       bvec->bv_offset = offset;

……

       bio->bi_vcnt++;

 

 

 

 

这几行代码将page、len等赋给一个新的io_vec,然后增加bi_vcnt的值。

 

 

 

 

当页中的块连续时:  一个bio包含许多blocks             

    Mpage_allock                  

   Bio_add_page                                                                               

   Mpage_bio_submit                

         Submit_bio

 

当页中的块不连续时:一个bio只包含一个block,然后在IO层可能进行重组

(这里每个bio应该只对应一个block,所以不需要类似Bio_add_page的函数)

b lock_read_full_page

submit_bh(bh和bio建立联系)

     submit_bio




buffer_head完全按照设备块来进行io,块大小取决于设备但是普遍比页面小,bh的元数据比率开销过大,bio则按照页面大小进行io,然而一个bio中可以包含多个页面,因此聚集的基于page的io吞吐量更大些,这就好比用桶提水比用汤勺舀水效率高一样。因此2.6内核普遍使用bio代替了bh,然而传统的bh并没有消失,只是它完全用bio来实现了,在读取小数据的时候,基于设备块来读取还是很可取的,因此内核干脆设计出一个 bh_lru缓存结构体来缓存8个最近使用并且猜测还将被使用的bh,这可能是一些块设备的元数据。在2.6内核中,bh退化成了一个接口层,虽然2.6内核完全使用了基于page的块io,但是却并没有丧失基于块映射io的高效性,只是这个工作交给了更底层的块设备驱动程序来做了,最终驱动程序还是成块成块地来读写数据的...早先对于bh来说,每一个块都要有一个bh结构体来描述,如果写一个很大的数据,就需要很多的bh链接成一个链,然后一个循环将它们全部写入设备,如果是bio的,使用mpage机制可以尽可能多地搜集很多的page,然后交给更底层的驱动。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值