在Linux下,驱动设备有字符设备驱动、块设备驱动和网络设备驱动三种,之前学习了字符设备驱动,现在开始学习块设备驱动。首先来比较看这两种设备驱动有何不通,为什么要分割成不同类型的驱动。举一个例子说明引入块设备驱动的必要:

假如按照字符设备一样的框架去构造驱动,如图:

块设备驱动_第1张图片

则需要频繁地对存储设备进行擦除,如果使用另一种框架构思(块设备思想):

块设备驱动_第2张图片

对比之下,对于这样的存储设备,则引入块设备显然使得操作的效果大为提高。由此可知块设备的特点就是:把请求放入队列,优化后执行。

字符设备与块设备I/O操作的区别:

a.块设备只能按照块为单位进行输入输出,而字符设备则是以字节为单位。大多数设备都是字符设备,因为它们不需要缓冲而且不是以固定块大小操作。

b.块设备对应I/O请求有对应的缓冲区,因此它们可以以选择任意顺序来响应,字符设备无需缓冲区且可以直接读写。对于存储设备而言,调整顺序十分重要,因为读写连续扇区比分离扇区快。

c.字符设备只能被顺序读写,而块设备可以随机访问。

    类比于字符设备,在块设备中,有一个类似于字符设备中file_operations的结构体:block_device_operations;在Linux内核中,使用一个gendisk结构体来表示一个独立的磁盘设备或分区;在Linux内核中,使用一个queue队列来管理这个设备的I/O请求。这就是块设备的大体构架。

    编写块设备驱动程序的框架主要为:

1.用alloc_disk(int minors);来分配gendisk结构体

2.设置gendisk结构体

3.分配/设置queue队列

4.设置gendisk的其他信息(如:容量)

5.注册gendisk结构体:add_disk

相关操作函数:

struct gendisk *alloc_disk(int minors);//分配gendisk结构体

void add_disk(struct gendisk *disk);//注册gendisk结构体

request_queue_t *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock);//分配队列,rfn为一个请求处理函数,lock是一个自旋锁

int register_blkdev(unsigned int major, const char *name);////注册块设备,name在注册驱动后,对应 cat /proc/devices里的名字

例子:

块设备驱动_第3张图片

块设备驱动_第4张图片

块设备驱动_第5张图片

以上就是块设备驱动程序的框架。块设备驱动程序相对于字符设备驱动程序要复杂,但对于像存储设备这样的块设备,这样的驱动构架可以提高效率。