通用块层相关数据结构

来源:互联网 发布:ping域名找不到主机 编辑:程序博客网 时间:2024/04/30 14:19

1.4.2 通用块层相关数据结构

好了,通用块层的一些比较重要的基础知识大家都知道了,下面我们着重来看bio这个东西。大家在前面“创建一个bio请求”一节中已经见过这个结构了,但是,由于它太重要了,所以我们这里有必要对它进行进一步的介绍。

 

每个bio结构都包含一个磁盘存储区标识符(存储区中的起始扇区号和扇区数目)和一个或多个描述与I/O操作相关的内存区的段。biobio数据结构描述:

 

struct bio {

       sector_t          bi_sector;    //I/O操作的第一个磁盘扇区

       struct bio        *bi_next;     //链接到请求队列中的下一个bio

       struct block_device *bi_bdev;//指向块设备描述符的指针

       unsigned long        bi_flags;  //bio的状态标志

       unsigned long        bi_rw;     //IO操作标志,即这次I.O是读或写

       unsigned short        bi_vcnt;   /* biobio_vec数组中段的数目 */

       unsigned short        bi_idx;           /* biobio_vec数组中段的当前索引值 */

 

       unsigned short        bi_phys_segments; //合并之后bio中物理段的数目

 

       unsigned short        bi_hw_segments; //合并之后硬件段的数目

 

       unsigned int           bi_size;   /* 需要传送的字节数 */

 

       unsigned int           bi_hw_front_size;// 硬件段合并算法使用

       unsigned int           bi_hw_back_size;// 硬件段合并算法使用

 

       unsigned int           bi_max_vecs;  /* biobio vec数组中允许的最大段数 */

 

       struct bio_vec        *bi_io_vec;     /*指向biobio_vec数组中的段的指针 */

 

       bio_end_io_t          *bi_end_io;   /* bioI/O操作结束时调用的方法 */

       atomic_t         bi_cnt;           /* bio的引用计数器 */

 

       void               *bi_private;   //通用块层和块设备驱动程序的I/O完成方法使用的指针

 

       bio_destructor_t     *bi_destructor;//释放bio时调用的析构方法(通常是bio_destructor()方法)r

};

 

在前面普通文件的readpage方法do_mpage_readpage中,biobi_sector是该页第一个块位于磁盘的逻辑块号blocks[0]左移(blkbits - 9)位,得到该块对应磁盘扇区号。前面讲了,扇区大小是512字节,一个块是1024字节,所以你可以猜到blkbits - 9肯定等于1。不错,blkbits就是10,这样1<<10=1024才是一个块的大小。

 

另外,回忆一下,在do_mpage_readpagebiobdev来自buffer_headb_bdev字段;bi_destructor被设置为bio_fs_destructor函数,作为当bio上的I/O操作完成时所执行的完成程序的地址;bi_io_vec通过bvec_alloc_bs函数初始化成数组,表示若干个bio待传输的段;同时bi_flagsbi_max_vecs字段也被设置了。另外min_t(int, nr_pages, bio_get_nr_vecs(bdev))表示这个bio中的iovec个数。所以,这里我们就来好好说道说道这个bio_vec数组。

 

bio中的每个段是由一个bio_vec数据结构描述的,bio中的bi_io_vec字段指向bio_vec数据结构的第一个元素,bi_vcnt字段则存放了bio_vec数组中当前的元素个数。

 

struct bio_vec {

       struct page      *bv_page;   //指向段的页框对应页描述符的指针

       unsigned int    bv_len;     //段的字节长度

       unsigned int    bv_offset;   //页框中段数据的偏移量

};

 

do_mpage_readpage调用mpage_alloc分配一个bio时,传递给mpage_alloc的参数是自buffer_headb_bdev字段,blocks[0]的扇区号,min_t(int, nr_pages, bio_get_nr_vecs(bdev))和等于GFP_KERNELgfp_flags

 

而这个min_t(int, nr_pages, bio_get_nr_vecs(bdev))这么一长串是什么呢?大家可以回过头去看看do_mpage_readpage函数:nr_pagesmpage_readpage传递给他的,其值是1;所以我们不用去管bio_get_nr_vecs的值了,min_t(int, nr_pages, bio_get_nr_vecs(bdev))这么一长串就是1,表示bio就只有一个iovec结构。什么意思?其实iovec结构是代表一个段,用于启动一次分散-聚集DMA传送,存放磁盘控制器所需的内存区的描述符链表的一项,包含一个地址和一个长度。

 

所以,对于普通文件,假设没有遇到文件的洞,那么一个页面所包含的4个块总是连续的,mpage_alloc调用的bio_alloc_bioset函数就只分配一个bio_vec结构。随后do_mpage_readpage通过调用bio_add_page将这个结构的bv_page指向对于的页描述符,bv_len设置为4个块的大小4096字节,bv_offset0bio_add_page还有一些合并段的工作,即不同的段在RAM中相应的页框正好是连续的并且在磁盘上相应的数据块也是相邻的,那么就合并它们。这些内容都比较绕脑子,有兴趣的同学可以去尝试深入分析一下。我们这里就假设一个页中4个块都是连续的,所以在bio_add_page函数的最后一行,把4个块的大小4096赋值给biobi_size字段。

原创粉丝点击