前言
研究IO也很久了,一直无法串联bio和块设备驱动,只知道bio经过IO调度算法传递到块设备驱动,怎么过去的,IO调度算法在哪里发挥作用,一直没有完全搞明白,查看了很多资料,终于对块设备驱动有所理解,也打通了bio到块设备。
一、传统块设备
我们先来实现一个基于内存的传统块设备驱动。
1.1 初始化一些东西
//暂时使用COMPAQ_SMART2_MAJOR作为主设备号,防止设备号冲突
#define SIMP_BLKDEV_DEVICEMAJOR COMPAQ_SMART2_MAJOR
//块设备名
#define SIMP_BLKDEV_DISKNAME "simp_blkdev"
//用一个数组来模拟一个物理存储
#define SIMP_BLKDEV_BYTES (16*1024*1024)
unsigned char simp_blkdev_data[SIMP_BLKDEV_BYTES];
static struct request_queue *simp_blkdev_queue;//请求队列
static struct gendisk *simp_blkdev_disk;//块设备
struct block_device_operations simp_blkdev_fops = {//块设备的操作函数
.owner = THIS_MODULE,
};
1.2 加载驱动
整个过程
1.创建request_queue(每个块设备一个队列),绑定函数simp_blkdev_do_request
2.创建一个gendisk(每个块设备就是一个gendisk)
3.将request_queue和gendisk绑定
4.注册gendisk
static int __init simp_blkdev_init(void)
{
int ret;
//初始化请求队列
simp_blkdev_queue = blk_init_queue(simp_blkdev_do_request, NULL);//这个方法将会在1.5仔细分析
simp_blkdev_disk = alloc_disk(1);//申请simp_blkdev_disk
//初始化simp_blkdev_disk
strcpy(simp_blkdev_disk->disk_name, SIMP_BLKDEV_DISKNAME);//设备名
simp_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR;//主设备号
simp_blkdev_disk->first_minor = 0;//副设备号
simp_blkdev_disk->fops = &simp_blkdev_fops;//块设备操作函数指针
simp_blkdev_disk->queue = simp_blkdev_queue;
//设置块设备的大小,大小是扇区的数量,一个扇区是512B
set_capacity(simp_blkdev_disk, SIMP_BLKDEV_BYTES>>9);
add_disk(simp_blkdev_disk);//注册simp_blkdev_disk
return 0;
}
1.3 simp_blkdev_do_request
1.调用调度算法的elv_next_request方法获得下一个处理的request
2.如果是读,将simp_blkdev_data拷贝到request.buffer,
3.如果是写,将request.buffer拷贝到simp_blkdev_data
4.调用end_request通知完成
static void simp_blkdev_do_request(struct request_queue *q)
{
struct request *req;
while ((req = elv_next_request(q)) != NULL) {//根据调度算法获得下一个request
switch (rq_data_dir(req)) {//判断读还是写
case READ:
memcpy(req->buffer, simp_blkdev_data + (req->sector << 9),
req->current_nr_sectors << 9);
end_request(req, 1);//完成通知
break;
case WRITE:
memcpy(simp_blkdev_data + (req->sector << 9),req->buffer,
req->current_nr_sectors << 9);
end_request(req, 1);//完成通知
break;
default:
/* No default because rq_data_dir(req) is 1 bit */
break;
}
}
1.4 卸载驱动
static void __exit simp_blkdev_exit(void)
{
del_gendisk(simp_blkdev_disk);//注销simp_blkdev_disk
put_disk(simp_blkdev_disk);//释放simp_blkdev_disk
blk_cleanup_queue(simp_blkdev_queue);//释放请求队列
}
千万别忘记下面代码
module_init(simp_blkdev_init);
module_exit(simp_blkdev_exit);
1.5 blk_init_queue
看了上面的代码,可能还是无法清晰的了解request_queue如何串联bio和块设备驱动,我们深入看一下
simp_blkdev_queue = blk_init_queue(simp_blkdev_do_request, N