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
 










