在Linux下,驱动设备有字符设备驱动、块设备驱动和网络设备驱动三种,之前学习了字符设备驱动,现在开始学习块设备驱动。首先来比较看这两种设备驱动有何不通,为什么要分割成不同类型的驱动。举一个例子说明引入块设备驱动的必要:
假如按照字符设备一样的框架去构造驱动,如图:
则需要频繁地对存储设备进行擦除,如果使用另一种框架构思(块设备思想):
对比之下,对于这样的存储设备,则引入块设备显然使得操作的效果大为提高。由此可知块设备的特点就是:把请求放入队列,优化后执行。
字符设备与块设备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里的名字
例子: