通用块层与bio

通用块层与bio

通用块层

什么是通用块层?

通用块层是一个内核组件,它的作用就是处理来自上层对所有块设备发出的io操作。例如,上层文件系统对磁盘上某个文件的读写操作都会转换成相应的io操作然后交给我们的通用块层进行处理。

bio结构

上面我们提到了很多次io操作的概念,,内核用一个叫做bio的结构体来描述一次块io操作。下面我们来分析一下bio的重要结构。

struct bio {
    sector_t        bi_sector;  /*我们想在块设备的第几个扇区上进行io操作(起始扇区),
    此处扇区大小是按512计算的*/

    struct bio      *bi_next;   
    struct block_device *bi_bdev;  /*指向块设备描述符的指针,该io操作是针对哪个块设备的*/
    unsigned long       bi_rw;  /*该io操作是读还是写*/
    unsigned short      bi_vcnt;  /* bio的bio_vec数组中段的数目 */
    unsigned short      bi_idx;  /* bio的bio_vec数组中段的当前索引值 */
    unsigned short      bi_phys_segments; //合并之后bio中(内存)物理段的数目
    unsigned int        bi_size;   /* 需要传送的字节数 */
    bio_end_io_t       *bi_end_io;   /* bio的I/O操作结束时调用的方法 */
    void               *bi_private;  //通用块层和块设备驱动程序的I/O完成方法使用的指针
    unsigned int        bi_max_vecs;  /* bio的bio vec数组中允许的最大段数 */
    atomic_t            bi_cnt;  /* bio的引用计数器 */
    struct bio_vec      *bi_io_vec;  /*指向bio的bio_vec数组中的段的指针 */
    struct bio_set      *bi_pool;
    struct bio_vec      bi_inline_vecs[0];/*一般一个bio就一个段,bi_inline_vecs就
    可满足,省去了再为bi_io_vec分配空间*/
}
什么是bio的段?

bio段就是描述所要读或写的数据在内存中位置。

struct bio_vec {
       struct page      *bv_page;   //指向段的页框对应页描述符的指针
       unsigned int    bv_len;     //段的字节长度,长度可以超过一个页
       unsigned int    bv_offset;   //页框中段数据的偏移量
};
bio与bio段的关系

一个bio可能有很多个bio段,这些bio段可能在内存上不连续(位于不同的页),但他们在磁盘上对应的位置是连续的。一般上层构建bio的时候都是只有一个bio段,可以参考_submit_bh函数。

在块io操作期间bio的内容一直保持更新,例如,块设备驱动在一次分散聚集DMA操作中不能一次完成全部数据的传送,那么bio的bi_idx就会更新来指向待传送的第一个段。

启发

我们完全可以绕过文件系统,绕过页高速缓存,直接构造我们的bio请求并交给通用块层,以此完成我们对磁盘(块设备)上数据的读取。
以下贴出我自己构造bio请求读磁盘的过程,亲测有效:

  1. struct bio *bio = bio_alloc(GFP_NOIO, 1); 首先通过bio_alloc分配一个bio结构。
  2. struct block_device *dev= lookup_bdev(“/dev/sdb”); 通过设备文件路径获得我们想读写的块设备的描述符。/dev/sdb是我的第二块磁盘,没有进行过格式化。
  3. 初始化bio,其中我们对bio中page调用了alloc_pages_current(GFP_KERNEL,0)来获得一个页框,bio->bi_end_io初始化为我们自己写的函数,bio请求完成时会触发该函数。

  4. blkdev_get(dev,FMODE_READ,0) 此步骤非常重要,其实bd_acquire(lookup_bdev会在内部调用它)确实可以获取一个block_device,根据inode的dev_t来确定。这里的block_device完全是个空壳,唯一的最重要信息就是dev_t的bdev->bd_dev,具体怎么和设备关联起来都是在blkdev_get做的。

  5. submit_bio(READ, bio);提交,上层通过它把构建好的bio交给通用块层进行处理,如果没有第四步,会出错,因为bio->bi_bdev->bd_disk会为NULL,正如4所说,而如果是/dev/sda则无影响,没有4依然可以,因为sda被文件系统格式化过,系统中有其他程序已经调用过blkdev_get将bdev和具体的设备关联在一起了。

  6. 最后bio请求(我们这里是读请求)完成后,调用我们自己定义的end_io函数,注意我们要在完成时记得将bio结构还给内核,通过调用bio_put完成归还。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值