1.3块设备驱动关键数据结构及函数API详细剖析
经过上节四个步骤我们已经熟悉并实战了一个最基本的过滤块设备驱动的设计技巧,我们这一节先不继续实战,我们本节把上节170行代码中接触到的块设备核心数据结构和API接口剖析一下,把这部分掌握和理解一下。
我们把上节涉及的六个数据结构和相关API接口罗列一下:
块设备核心数据结构
gendisk |
块设备仓库 |
hd_struct |
块设备分区 |
block_device |
文件系统层使用的块设备描述符 |
request_queue |
仓库的关卡(请求队列) |
request |
包含多个bio的大请求 |
bio |
单个请求 |
块设备核心API接口
register_blkdev |
注册并申请门牌号 |
alloc_disk |
申请仓库 |
blk_alloc_queue |
申请仓库的关卡 |
blk_queue_make_request |
注册仓库的加工处理函数 |
add_disk |
将申请的仓库注册到内核中,成为合法仓库 |
结合上节块设备在Linux中的总体结构图来看,我们再贴一下这个图,根据这个图我们将请求从文件系统层构建出bio开始,直到进入到请求处理函数,分析一下其过程,这个过程会扩展描述到核心数据结构中的几个关键字段,大家先试着熟悉,然后我们会给出核心数据结构的嵌套关系图,让大家更清楚的认识一下各个核心数据结构之间的关系,最后我们会详细剖析各个数据结构和API接口功能。
上层文件系统发来I/O请求时,我们在块设备驱动的请求处理函数make_request上接收到的是bio,每个bio结构中都包含了一个bio_vec数组。bio_vec是用于记录一段连续内存空间位置信息的数据结构,包括描述这段内存连续空间的页指针描述符bv_page,数据长度bv_len,数据在一个页中的开始位置bv_offset。如此分析我们知道bio请求包含了一个bio_vec数组,意味着包含了一组内存连续空间。
接下来文件系统层调用通用块层的generic_make_request函数,将请求插入到仓库的关卡即请求队列上request_queue,如果队列没有使用,则继续调用到我们注册的请求处理函数make_request上。
请求队列request_queue中的每一个元素是一个请求集合request,request包含了多个bio请求,同时多个request通过链表链接在一起,链表头在request_queue上;同样request中的多个bio也通过链表链接在一起。
磁盘描述符gendisk通过指向该磁盘的请求队列的指针queue与其请求队列关联起来。内核用结构block_device代表一个块设备对象,它是文件系统层使用的数据结构,如:整个硬盘或特定分区都是一个块设备对象。如果该结构代表一个分区,则其成员bd_part指向设备的分区结构;如果该结构代表设备,则其成员bd_disk指向设备的通用硬盘结构gendisk。
根据上面的描述,我们把数据结构关系画一下,如下,大家可以更清楚的看一下各个结构之间的关系,也更加能够从整体上把握IO请求在操作系统内核中的描述和处理。
根据上面这个图,让我们可以继续总结一下,充分把握好它们的结构关系。块设备会有一个仓库描述gendisk,如果仓库有分区,则分区由hd_struct描述,同时文件系统会对仓库及分区都用一个独立的block_device进行描述;文件系统产生bio请求,多个bio会组装成一个request,多个request会组装到request_queue请求队列上。
好了,至此相信大家能够很牢固的记住各结构之间的关系了,并且能够根据上图从整体上把握好应用层数据读写请求在操作系统内核中的处理关系,下面我们详细剖析一下各个数据结构及API的功能,大家可以作为一个参考,再后面实战时可以继续回来进行查阅学习。
block_device关键成员剖析
类型 |
字段 |
说明 |
dev_t |
bd_dev |
块设备的主设备号和次设备号 |
struct inode* |
bd_inode |
指向bdev文件系统中块设备对应的文件索引节点的指针 |
int |
bd_openers |
计数器,统计块设备已经被打开了多少次 |
struct mutex |
bd_mutex |
打开或关闭的互斥量 |
struct list_head |
bd_inodes |
已打开的块设备文件的索引节点链表的首部 |
void* |
bd_holders |
块设备描述符的当前所有者 |
struct block_device* |
bd_contains |
如果块设备是一个分区,则指向整个磁盘的块设备描述符;否则,指向该块设备描述符 |
unsigned |
bd_block_size |
块大小 |
struct hd_struct* |
bd_part |
指向分区描述符的指针(如果该块设备不是一个分区,则为NULL) |
unsigned |
bd_part_count |
计数器,统计包含在块设备中的分区已经被打开了多少次 |
struct gendisk* |
bd_disk |
指向块设备中基本磁盘的gendisk结构的指针 |
struct list_head |
bd_list |
用于块设备描述符链表的指针 |
unsigned long |
bd_private |
指向块设备持有者的私有数据的指针 |
hd_struct关键成员剖析
类型 |
字段 |
说明 |
sector_t |
start_sect |
磁盘中分区的起始扇区 |
sector_t |
nr_sects |
分区的长度(总共的扇区数) |
int |
policy |
如果分区是只读的,则置为1;否则为0 |
int |
partno |
磁盘中分区的相对索引 |
gendisk关键成员剖析
类型 |
字段 |
说明 |
int |
major |
磁盘主设备号, 每个块设备都有唯一的主设备号,在这个块设备上建立的分区都使用这个相同的主设备号。具有相同主设备号的设备,使用相同的驱动程序。 |
int |
first_minor |
与磁盘关联的第一个次设备号。在某一个设备上首先 |