Linux内核struct bio中块设备扇区与内存页的映射问题
我正在Ubuntu 20.04(内核版本5.4.0)上开发自定义设备映射器,需将读取的数据缓存到内核空间内存缓冲区,以扇区粒度为后续读请求提供服务(类似页缓存)。为此,需要建立sector_t(块设备扇区号)与数据的映射关系。已知相关信息可从linux/blk_types.h中定义的struct bio获取,其中sector_t bi_sector指定块设备上的读取位置,数据会被读入struct bio_vec *bi_io_vec,但不清楚数据的哪部分对应哪个扇区,现验证以下猜想:
猜想1:
struct bio仅含一个bi_sector,但单个bio的读取大小可超过512B(扇区大小),是否bi_sector是连续待读扇区的起始编号?
答案:是的,bi_sector就是当前bio请求对应块设备上的起始扇区号。内核中bio请求的扇区都是连续的,一个bio对应一段连续的块设备地址空间,总长度由bio_sectors(bio)计算得出(返回扇区数量),后续扇区依次为bi_sector + 1、bi_sector + 2……直到覆盖整个bio的长度。猜想2:读取的数据在
bi_io_vec中是否为线性映射?例如读取4KB数据,bi_io_vec含两个struct bio_vec段,数据是否按顺序存储在(bi_io_vec[0].bv_page + bi_io_vec[0].bv_offset, bi_io_vec[0].bv_page + bi_io_vec[0].bv_offset + bi_io_vec[0].bv_len)和(bi_io_vec[1].bv_page + bi_io_vec[1].bv_offset, bi_io_vec[1].bv_page + bi_io_vec[1].bv_offset + bi_io_vec[1].bv_len)内存区域?
答案:完全正确。bi_io_vec中的各个段按逻辑顺序排列,对应bio请求数据的线性偏移。第一个bio_vec对应起始的一段数据,第二个接着第一段的末尾,以此类推。遍历bio_vec并依次拷贝每个段的数据,就能得到连续的、对应扇区顺序的数据。猜想3:从bio中读取数据时,使用
char *bio_data(struct bio *bio)是否不安全?是否需要遍历每个bio_vec来收集数据?
答案:bio_data()仅在bio的所有数据都位于一个连续内存块时有效,如果bio是分散-聚合(scatter-gather)类型(包含多个bio_vec段),bio_data()返回的指针不可靠,甚至会引发内核崩溃。因此在你的场景中,必须遍历每个bio_vec,通过kmap()/kunmap()(或page_address(),针对非高端内存页)获取每个页的地址,再结合bv_offset和bv_len读取对应数据。
内容的提问来源于stack exchange,提问作者linkfire

