字符设备驱动程序
应用程序是调用C库中的open read write等函数。而为了操作硬件,所以引入了驱动模块。
构建一个简单的驱动,有一下步骤。
1. 创建file_operations
2. 申请设备号
3. 注册字符设备驱动,
4. 驱动入口
5. 驱动出口
检查数据是否到来的方式:
1. 查询方式
2. 休眠唤醒方式
如果设备出现异常而无法唤醒时,则将永远处于休眠状态。
3. poll机制
如果没有被唤醒,则在一定时间内可自己唤醒。
4. 异步通知(信号)
而以上的几种方式通用性不高,为了增强通用性,使用输入子系统。
块设备驱动程序
在读取硬盘的时候,磁头需要定位,但是定位是非常损耗时间的。
flash的操作也是一样的。在写之前需要擦除整块。
如果我们现在进行一下操作:1.写扇区0 2.写扇区1
1. 写扇区0:
读出整块到buffer
修改buffer中相应的内容,
擦除整块
写入整块
2.读扇区1则是重复上述步骤。
而上述的方法在操作的时候太过于繁杂,浪费时间。所以操作系统在处理的时候优化了一次。
1. 把读写放入队列
2.调用队列的处理函数(优化/调顺序/合并)
块设备驱动程序框架:
---------------------------------------------------------(文件的读写)
app: open、write、read等 (把对文件的读写转化为对扇区的读写)
----------------------------------------------------------
文件系统: vfat、ext2、ext3、yaffs2、jffs2
------------------ ll_rw_block --------------------------
块设备驱动程序
----------------------------------------------------------
硬件: 硬盘、Flash
----------------------------------------------------------
分析 ll_rw_block :(1. 把“读写”放入队列 2. 调用队列的处理函数( 优化 / 调顺序 / 合并 ))
for (i = 0; i < nr; i++) {
struct buffer_head *bh = bhs[i];
submit_bh(WRITE, bh);
struct bio *bio; //使用bh来构造bio (block input/output)
submit_bio(rw, bio); //通用的构造请求:使用bio来构造请求(request)
generic_make_request(bio);
__generic_make_request(bio);
request_queue_t *q = bdev_get_queue(bio->bi_bdev); //找到队列
ret = q->make_request_fn(q, bio); //默认函数是__make_request
__make_request
elv_merge(q, &req, bio); //尝试合并
init_request_from_bio(req, bio); //如果尝试合并不成功 使用bio构造请求
add_request(q, req); //把请求放入队列
__generic_unplug_device(q); //执行队列
q->request_fn(q); //调用队列的处理函数
}
如何编写块设备驱动程序呢?
1. 分配gendisk
2. 设置
2.1分配/设置队列: request_queue_t //提供读写能力
blk_init_queue
2. 2 设置gendisk其他信息 //提供属性:容量,扇区大小等
3. 注册: add_disk