分类: LINUX
2012-07-01 20:46:52
今天主要学习了第十三章:块设备驱动
1 字符设备和块设备区别
(1)字符设备是按字节访问,块设备只能一次传递一块或者多块数据。
(2)块设备对于I/O请求有对应的缓冲区,因此他们可以选择以什么样的顺序进行响应,但字符设备无须缓冲且被直接读写。
(3)块设备是随机访问的,而字符设备是按照字符流顺序访问。
2 Linux块设备驱动模块加载与卸载
在块设备驱动的模块加载函数中通常需要完成如下工作:
① 分配、初始化请求队列,绑定请求队列和请求函数。
② 分配、初始化gendisk,给gendisk的major、fops、queue等成员赋值,最后添加gendisk。
③ 注册块设备驱动。
在块设备驱动的模块卸载函数中通常需要与模块加载函数相反的工作:
① 清除请求队列。
② 删除gendisk和对gendisk的引用。
③ 删除对块设备的引用,注销块设备驱动。
3 块设备驱动I/O请求处理
3.1使用请求队列
块设备驱动请求函数的原型为:
void request(request_queue_t *queue);
这个函数不能由驱动自己调用,只有当内核认为是时候让驱动处理对设备的读写等操作时,它才调用这个函数。
请求函数可以在没有完成请求队列中的所有请求的情况下返回,甚至它1个请求不完成都可以返回。但是,对大部分设备而言,在请求函数中处理完所有请求后再返回通常是值得推荐的方法。
3.2不使用请求队列
使用请求队列对于一个机械的磁盘设备而言的确有助于提高系统的性能,但是对于许多块设备,如数码相机的存储卡、RAM盘等完全可真正随机访问的设备而言,无法从高级的请求队列逻辑中获益。对于这些设备,块层支持“无队列”的操作模式,为使用这个模式,驱动必须提供一个“制造请求”函数,而不是一个请求函数,“制造请求”函数的原型为:
typedef int (make_request_fn) (request_queue_t *q, struct bio *bio);
上述函数的第1个参数仍然是“请求队列”,但是这个“请求队列”实际不包含任何请求。因此,“制造请求”函数的主要参数是bio结构体,这个bio结构体表示1个或多个要传送的缓冲区。“制造请求”函数或者直接进行传输,或者把请求重定向给其它设备。
在“制造请求”函数在处理完成后应该使用bio_endio()函数通知处理结束:
void bio_endio(struct bio *bio, unsigned int bytes, int error);
参数bytes 是已经传送的字节数,它可以比这个bio所代表的字节数少,这意味着“部分完成”,同时bio结构体中的当前缓冲区指针需要更新。当设备进一步处理这个bio后,驱动应该再次调用 bio_endio(),如果不能完成这个请求,应指出一个错误,错误码赋值给error参数。
不管对应的I/O处理成功与否,“制造请求”函数都应该返回0。如果“制造请求”函数返回一个非零值,bio 将被再次提交。
4 一些重要的概念关系
一个struct bio 代表一个块设备的I/O 请求。I/O调度器可以将连续的bio合并成一个请求:struct request。I/O请求形成的队列叫struct request_queue。
5 一个块设备的例子
点击(此处)折叠或打开
6 块设备测试步骤
# insmod simp_blkdev.ko
# ls -l /dev/simp_blkdev
# mkfs.ext3 /dev/simp_blkdev
# mkdir -p /mnt/temp1
# mount /dev/simp_blkdev /mnt/temp1
# cp /etc/init.d/* /mnt/temp1
# ls /mnt/temp1
# umount /mnt/temp1
# rmmod simp_blkdev
参考文献:
《写一个块设备驱动》