1.什么是bio
block io
,内核IO子系统中最重要的一个结构体struct bio
,内核需要读写文件时,会构造一个bio结构体,将此bio结构体放到io队列中。结构体定义如下
/*
* main unit of I/O for the block layer and lower layers (ie drivers and
* stacking drivers)
*/
struct bio {
struct bio *bi_next; /* request queue link */
struct gendisk *bi_disk;
unsigned int bi_opf; /* bottom bits req flags,
* top bits REQ_OP. Use
* accessors.
*/
unsigned short bi_flags; /* status, etc and bvec pool number */
unsigned short bi_ioprio;
unsigned short bi_write_hint;
blk_status_t bi_status;
u8 bi_partno;
atomic_t __bi_remaining;
struct bvec_iter bi_iter;
bio_end_io_t *bi_end_io;
void *bi_private;
#ifdef CONFIG_BLK_CGROUP
/*
* Represents the association of the css and request_queue for the bio.
* If a bio goes direct to device, it will not have a blkg as it will
* not have a request_queue associated with it. The reference is put
* on release of the bio.
*/
struct blkcg_gq *bi_blkg;
struct bio_issue bi_issue;
#ifdef CONFIG_BLK_CGROUP_IOCOST
u64 bi_iocost_cost;
#endif
#endif
#ifdef CONFIG_BLK_INLINE_ENCRYPTION
struct bio_crypt_ctx *bi_crypt_context;
#endif
union {
#if defined(CONFIG_BLK_DEV_INTEGRITY)
struct bio_integrity_payload *bi_integrity; /* data integrity */
#endif
};
unsigned short bi_vcnt; /* how many bio_vec's */
/*
* Everything starting with bi_max_vecs will be preserved by bio_reset()
*/
unsigned short bi_max_vecs; /* max bvl_vecs we can hold */
atomic_t __bi_cnt; /* pin count */
struct bio_vec *bi_io_vec; /* the actual vec list */
struct bio_set *bi_pool;
KABI_RESERVE(1)
KABI_RESERVE(2)
KABI_RESERVE(3)
KABI_RESERVE(4)
/*
* 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[];
};
2.struct bio结构体
网上讲bio结构体的很多,讲的也不是很明白,我写一下我自己学习的理解。
其实就是看内核读写磁盘时,需要提供哪些信息可以支撑本次请求。其实也简单,无论读写,都需要提供两个信息:
内存信息:本次请求,内存数据来源(写)/目的(读)。相关的信息包括
struct bio_vec *bi_io_vec;
//描述内存信息,包含struct page、len、offset。这里可以是一个数组,数组中一个元素描述一个内存片段
unsigned short bi_vcnt;
//指示bi_io_vec由多少个数据(即多少个内存片段)
磁盘信息:本次请求,磁盘操作哪个位置。由两个字段来描述
struct gendisk *bi_disk;
//用来说明操作哪个磁盘设备
struct bvec_iter;
//用来描述操作的扇区位置、大小等
历史沿革:这部分信息在低版本linux(如linux-0.11
)中由struct buffer_head
结构体来描述,最新版本内核调整到bio描述,buffer_head还在,但是变成了其他用途了
3.一个struct bio结构体能描述几个io请求
一个bio只能描述一个io请求,如果由多个io请求,可以通过struct bio *bi_next;
串联起来
一个io请求是指操作一个连续磁盘位置一次。例如:一个8k的文件,文件系统block块是4k,磁盘sector是512字节,这个文件在文件系统层面分成不连续的两个block存储,每个block占用8个连续的sector,那么这一次8k的读请求需要构造两个bio请求。
4.关于分散读(scatter read)和聚集写(gather write)
一个bio请求在磁盘侧对应一个连续位置,但是对于内存侧可以有多个位置,由struct bio_vec *bi_io_vec
数组来描述,数组中一个元素表示一个内存片段。读写是对称的,即可以将多个内存片段逐个写入连续磁盘位置;或者从磁盘连续位置读到内存的多个内存片段中。这样做的好处是灵活,同时减小了拷贝次数。想象一下,如果内存侧也需要是连续的空间,那么对于分散的读写,或者readv/writev这样的操作就需要先在内存中进行拷贝操作。
注意,无论读写,都需要填写struct bio_vec,指示内存数据来源(写)/目的(读)。
5.关于内存描述部分的几个成员
(1)性能优化
struct bio_vec *bi_io_vec;
//内存侧片段
struct bio_vec bi_inline_vecs[];
//柔性数组
在内存片段比较少的时候(低于BIO_INLINE_VECS(4)个片段,实际的绝大多数场景),可以直接在柔性数组中分配struct bio_vec bi_inline_vecs[]
,然后将bi_io_vec
指向bi_inline_vecs
柔性数组,这样在分配bio时,就直接分配好了bio_vec结构体,减少一次内存分配;同时也减少了一点内存碎片。
(2)pagecache
struct bio_vec的定义如下
struct bio_vec {
struct page *bv_page; //注意这里是物理页框
unsigned int bv_len;
unsigned int bv_offset;
}
此处的bv_page对应的页面,就是我们平时说的pagecache,也就是页缓存。例外情况:
- direct io:open时指定O_DIRECT标志
- 裸设备io:例如直接读取/dev/sda