块设备剖析之BIO

本文详细介绍了Linux内核版本3.2.40中BIO(Block I/O)结构体的基本组成及bi_io_vec向量的应用。BIO是块设备I/O操作的基础单元,通过bi_io_vec描述内存中数据分布。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文链接:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=30282771&id=5144166

本文所有内容均基于内核版本Linux-v3.2.40。


    本文主要对BIO结构体作基本说明,并重点分析bi_io_vec向量及其用法。

    1. BIO结构体如下所示:

struct bio {
    sector_t        bi_sector;   /* 本次IO操作的其实扇区,扇区都是512字节大小 */
    struct bio        *bi_next;    /* 用于链接处于同一个request中的BIO */
    struct block_device    *bi_bdev; /* 该bio所请求的块设备 */
    unsigned long        bi_flags;    /* 状态和命令标志 */
    unsigned long        bi_rw;        /* 标志位,主要用于区分读写*/

    unsigned short        bi_vcnt;    /* vec向量数组中向量的个数 */
    unsigned short        bi_idx;        /* vec数组中当前处理的向量索引 */

    /* Number of segments in this BIO after
     * physical address coalescing is performed.
     */
    unsigned int        bi_phys_segments;  /* 合并后的片段数目 */

    unsigned int        bi_size;    /* 本BIO数据量,以字节为单位 */

    /*
     * 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; /* 第一个可合并段的大小,与request合并相关 */
    unsigned int        bi_seg_back_size; /* 最后一个可合并段的大小,与request合并相关 */

    unsigned int        bi_max_vecs;    /* vec向量数组中向量元素个数的上限 */

    atomic_t        bi_cnt;        /* 使用计数 */

    struct bio_vec        *bi_io_vec;    /* vec向量数组指针 */

    bio_end_io_t        *bi_end_io;  /* 该bio结束时的回调函数,一般用于通知调用者该bio的完成情况 */

    void            *bi_private; /* 私有指针,通用bio代码不会使用该成员,一般供底层驱动程序使用 */
#if defined(CONFIG_BLK_DEV_INTEGRITY)
    struct bio_integrity_payload *bi_integrity; /* data integrity */
#endif

    bio_destructor_t    *bi_destructor;    /* 析构函数,用于在删除一个bio实例时调用 */

    /*
     * 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];
};
 注意:
    1). 一个BIO所请求的数据在块设备中是连续的,对于不连续的数据块需要放到多个BIO中。
    2). 一个BIO所携带的数据大小是有上限的,该上限值由bi_max_vecs间接指定,超过上限的数据块必须放到多个BIO中。

    2. 向量数组
bi_io_vec

    BIO中的向量数组bi_io_vec用来描述该BIO所请求的数据在内存中的分布,其中每个数组元素的具体结构如下:

struct bio_vec {
    struct page    *bv_page; /* 在内存中的page实例 */
    unsigned int    bv_len; /* 该向量元素关联的数据长度,最大为一个page */
    unsigned int    bv_offset; /* 该向量元素关联的数据在page中的偏移 */
};
数组个数由bi_vcnt指定,而bi_idx则表示当前处理的vec索引,因此根据而且即可跟踪该BIO的处理进度。
    每个vec对应一个page,它所描述的数据长度由bv_len指定,而bv_offset则用来说明数据块在page中的偏移。

     2.1 两个常见的疑问
    1)为什么需要bv_offsetbv_len呢?(我们假定以下的讨论中page size都为4K,当然对于其他的page size,道理是一样的
    我们知道对于块设备来说,它处理数据的最小单位是扇区,而不同介质的扇区大小也是不同的,定义bv_offset和bv_len就是为了将数据块的粒度切分到扇区大小。例如机械硬盘,它的扇区大小为512,所以为了描述一个扇区,bv_len必须设置为512,而bv_offset可以为0/512/1024等等,总之是512的倍数且不超过一个page,也就是说bv_len和bv_offset必须是扇区对齐的。
    2)在用户态操作一个块设备的时候不是也能读写几个字节?
    其实这涉及到文件的cache操作,当从应用层下发几字节的读请求时,文件系统会向块设备下发4k甚至更多的读请求,待块设备返回后,将返回的数据缓存到内存中,并向应用层返回请求的数据,这也是常说的buffer-IO模式;对于另一种模式dircet-IO,是不能读写几个字节的,它请求的数据必须是扇区对齐的。

     2.2 bi_io_vec的遍历
    内核提供了宏bio_for_each_segment用来遍历bi_io_vec,其实就是借助了bi_vcnt和bi_idx。

     2.3 BIO、bi_io_vec、page之间的关系



    本文如有不妥或错误之处,欢迎批评指正。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值