分类: LINUX
2015-03-05 23:39:30
内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区。如果该结构代表一个分区,则其成员bd_part指向设备的分区结构。如果该结构代表设备,则其成员bd_disk指向设备的通用硬盘结构gendisk
当用户打开块设备文件时,内核创建结构block_device实例,设备驱动程序还将创建结构gendisk实例,分配请求队列并注册结构block_device实例。
块设备对象结构block_device列出如下(在include/linux/fs.h中)
结构体gendisk代表了一个通用硬盘(generic hard disk)对象,它存储了一个硬盘的信息,包括请求队列、分区链表和块设备操作函数集等。块设备驱动程序分配结构gendisk实例,装载分区表,分配请求队列并填充结构的其他域。
支持分区的块驱动程序必须包含
结构gendisk列出如下(在include/linux/genhd.h中):
Linux内核提供了一组函数来操作gendisk,主要包括:
分配gendisk
struct gendisk *alloc_disk(int minors);
minors 参数是这个磁盘使用的次设备号的数量,一般也就是磁盘分区的数量,此后minors不能被修改。
增加gendisk
gendisk结构体被分配之后,系统还不能使用这个磁盘,需要调用如下函数来注册这个磁盘设备:
void add_disk(struct gendisk *gd);
特别要注意的是对add_disk()的调用必须发生在驱动程序的初始化工作完成并能响应磁盘的请求之后。
释放gendisk
当不再需要一个磁盘时,应当使用如下函数释放gendisk:
void del_gendisk(struct gendisk *gd);
设置gendisk容量
void set_capacity(struct gendisk *disk, sector_t size);
块设备中最小的可寻址单元是扇区,扇区大小一般是2的整数倍,最常见的大小是512字节。扇区的大小是设备的物理属性,扇区是所有块设备的基本单元,块设备 无法对比它还小的单元进行寻址和操作,不过许多块设备能够一次就传输多个扇区。虽然大多数块设备的扇区大小都是512字节,不过其它大小的扇区也很常见, 比如,很多CD-ROM盘的扇区都是2K大小。不管物理设备的真实扇区大小是多少,内核与块设备驱动交互的扇区都以512字节为单位。因此,set_capacity()函数也以512字节为单位。
分区结构hd_struct代表了一个分区对象,它存储了一个硬盘的一个分区的信息,驱动程序初始化时,从硬盘的分区表中提取分区信息,存放在分区结构实例中。
字符设备通过 file_operations 操作结构使它们的操作对系统可用. 一个类似的结构用在块设备上是 struct block_device_operations,
定义在
int (*open)(struct inode *inode, struct file *filp);
int (*release)(struct inode *inode, struct file *filp);
就像它们的字符驱动对等体一样工作的函数; 无论何时设备被打开和关闭都调用它们. 一个字符驱动可能通过启动设备或者锁住门(为可移出的介质)来响应一个 open 调用. 如果你将介质锁入设备, 你当然应当在 release 方法中解锁.
int (*ioctl)(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg);
实现 ioctl 系统调用的方法. 但是, 块层首先解释大量的标准请求; 因此大部分的块驱动 ioctl 方法相当短.
PS:在block_device_operations中没有实际读或写数据的函数. 在块 I/O 子系统, 这些操作由请求函数处理
结构request代表了挂起的I/O请求,每个请求用一个结构request实例描述,存放在请求队列链表中,由电梯算法进行排序,每个请求包含1个或多个结构bio实例
request结构体的主要成员包括:
sector_t hard_sector;
unsigned long hard_nr_sectors;
unsigned int hard_cur_sectors;
上述3个成员标识还未完成的扇区,hard_sector是第1个尚未传输的扇区,hard_nr_sectors是尚待完成的扇区数,hard_cur_sectors是并且当前I/O操作中待完成的扇区数。这些成员只用于内核块设备层,驱动不应当使用它们。
sector_t sector;
unsigned long nr_sectors;
unsigned int current_nr_sectors;
驱动中会经常与这3个成员打交道,这3个成员在内核和驱动交互中发挥着重大作用。它们以512字节大小为1个扇区,如果硬件的扇区大小不是512字节,则需要进行相应的调整。例如,如果硬件的扇区大小是2048字节,则在进行硬件操作之前,需要用4来除起始扇区号。
hard_sector、hard_nr_sectors、hard_cur_sectors与sector、nr_sectors、current_nr_sectors之间可认为是“副本”关系。
struct bio *bio;
bio是这个请求中包含的bio结构体的链表,驱动中不宜直接存取这个成员,而应该使用后文将介绍的rq_for_each_bio()。
每个块设备都有一个请求队列,每个请求队列单独执行I/O调度,请求队列是由请求结构实例链接成的双向链表,链表以及整个队列的信息用结构request_queue描述,称为请求队列对象结构或请求队列结构。它存放了关于挂起请求的信息以及管理请求队列(如:电梯算法)所需要的信息。结构成员request_fn是来自设备驱动程序的请求处理函数。
请求队列结构request_queue列出如下(在/include/linux/blk_dev.h中)
太长了,此处略,其实也看不懂,- -#
通常1个bio对应1个I/O请求,IO调度算法可将连续的bio合并成1个请求。所以,1个请求可以包含多个bio。
内核中块I/O操作的基本容器由bio结构体表示,定义 在
bio为通用层的主要数据结构,既描述了磁盘的位置,又描述了内存的位置,是上层内核vfs与下层驱动的连接纽带
结构bio_vec代表了内存中的一个数据段,数据段用页、偏移和长度描
述。I/O需要执行的内存位置用段表示,结构bio指向了一个段的数组。
结构bio_vec列出如下(在include/linux/bio.h中):
struct bio_vec {
struct page *bv_page; /*数据段所在的页*/
unsigned short bv_len; /*数据段的长度*/
unsigned short bv_offset; /*数据段页内偏移*/
};
块设备各个结构体间关系