《Linux内核设计与实现》读书笔记——块IO层

块设备

系统中能够随机访问固定大小数据片的硬件设备成为块设备。比如硬盘,软盘,光盘,闪存等。

另一种基础设备类型是字符设备。比如键盘,串口等。

块设备最小的寻址单元是扇区。

扇区大小是2的整数倍,一般是512字节。

扇区的大小是设备的物理属性,扇区是所有块设备的基本单元。

内核执行的所有磁盘操作是按照进行的。

块不能比扇区小,只能是数倍于扇区的大小。

块不能超过一个页的长度。

块的大小通常是512字节,1KB和4KB。

块和扇区的关系:

 

缓冲区

当一个块调入内存时,它要存储在一个缓冲区中。

缓冲区相当于磁盘块在内存中的表示。

一个页可以容纳一个或多个内存中的块。

每个缓冲区都有一个对应的描述符,用buffer_head结构体表示,称为缓冲区头(include\linux\buffer_head.h):

struct buffer_head {
    unsigned long b_state;      /* buffer state bitmap (see above) */
    struct buffer_head *b_this_page;/* circular list of page's buffers */
    struct page *b_page;        /* the page this bh is mapped to */
    sector_t b_blocknr;     /* start block number */
    size_t b_size;          /* size of mapping */
    char *b_data;           /* pointer to data within the page */
    struct block_device *b_bdev;
    bh_end_io_t *b_end_io;      /* I/O completion */
    void *b_private;        /* reserved for b_end_io */
    struct list_head b_assoc_buffers; /* associated with another mapping */
    struct address_space *b_assoc_map;  /* mapping this buffer is
                           associated with */
    atomic_t b_count;       /* users using this buffer_head */
};

b_state:表示缓冲区的状态,是下面标志中的一种或多种的组合。

还有一个bh_state是BH_PrivateStart,该标志不是可用状态的标志,使用它是为了指明可被其它代码使用的起始位。

b_count:表示缓冲区的使用计数,通过get_bh()和put_bh()来增加或者减少,在操作缓冲区头之前应该使用get_bh()函数增加缓冲区头的引用计数,确保该缓冲区头不会再被分配出去。

b_blocknr:表示与缓冲区对应的磁盘物理块。

b_bdev:指明块设备中的逻辑块号。

b_page:表示与缓冲区对应的内存物理页。

b_data:指向相应的块。

b_size:表示块的大小。

缓冲区头描述了磁盘块和物理内存缓冲区之间的映射关系。

 

bio

内核中块IO操作的基本容器由bio结构体表示。

该结构体代表了正在现场的(活动的)以片段(segment)链表形式组织的块IO操作。

一个片段是一小块连续的内存缓冲区,这样就不需要保证单个缓冲区一定要连续。

通过片段来描述缓冲区,即使缓冲区分散在内存的多个位置上,bio结构体也能对内核保证IO操作的执行。

bio结构体如下(include\linux\bio.h):

struct bio {
    sector_t        bi_sector;  /* device address in 512 byte
                           sectors */
    struct bio      *bi_next;   /* request queue link */
    struct block_device *bi_bdev;
    unsigned long       bi_flags;   /* status, command, etc */
    unsigned long       bi_rw;      /* bottom bits READ/WRITE,
                         * top bits priority
                         */
    unsigned short      bi_vcnt;    /* how many bio_vec's */
    unsigned short      bi_idx;     /* current index into bvl_vec */
    /* Number of segments in this BIO after
     * physical address coalescing is performed.
     */
    unsigned int        bi_phys_segments;
    unsigned int        bi_size;    /* residual I/O count */
    /*
     * To keep track of the max segment size, we account for the
     * sizes of the first and last mergeable segments in this bio.
     */
    unsigned int        bi_seg_front_size;
    unsigned int        bi_seg_back_size;
    unsigned int        bi_max_vecs;    /* max bvl_vecs we can hold */
    unsigned int        bi_comp_cpu;    /* completion CPU */
    atomic_t        bi_cnt;     /* pin count */
    struct bio_vec      *bi_io_vec; /* the actual vec list */
    bio_end_io_t        *bi_end_io;
    void            *bi_private;
#if defined(CONFIG_BLK_DEV_INTEGRITY)
    struct bio_integrity_payload *bi_integrity;  /* data integrity */
#endif
    bio_destructor_t    *bi_destructor; /* destructor */
    /*
     * We can inline a number of vecs at the end of the bio, to avoid
     * double allocations for a small number of bio_vecs. This member
     * MUST obviously be kept at the very end of the bio.
     */
    struct bio_vec      bi_inline_vecs[0];
};

bi_io_vec指向一个bio_vec结构图链表,它包含了一个特定IO操作锁需要使用到的所有片段(include\linux\bio.h):

struct bio_vec {
    struct page *bv_page;
    unsigned int    bv_len;
    unsigned int    bv_offset;
};

bv_page是片段所在的物理页,bv_len是块在物理页中的偏移地址,bv_offset是给定偏移量开始的块长度。通过它可以看出bio描述的块不需要连续存储。

bi_vcnt表示的bi_io_vec指向的bio_vec的个数。

bi_idx是指向bi_io_vec的当前索引。

bi_cnt记录bio结构体的引用计数,为0时就应该撤销该bio结构体。

每一个块IO请求都通过一个bio结构体表示。

 

IO调度

块设备将它们挂起的块IO请求保存在请求队列,它有结构体request_queue表示。

每个请求request可以由多个bio结构体组成。

磁盘寻址是整个计算机中最慢的操作之一。

为了优化寻址操作,内核既不会简单地按请求接收次序,也不会立即将其提交给磁盘。

会先进行合并排序的预操作。

IO调度程序负责提交IO请求,它的工作是管理块设备的请求队列。

IO调度算法类似与电梯,因此也称电梯调度。

目前有的四种调度:

  • Linus电梯IO调度
  • 最终期限IO调度
  • 完全公正的排队IO调度
  • 空操作IO调度

 

猜你喜欢

转载自blog.csdn.net/jiangwei0512/article/details/106150991