以下代码亲测在2.6.38上测试通过,在3的内核上不行,因为3的内核有许多API已经变了(见上篇),而且3的struct request没有buffer这个成员
simp_blkdev.c:
#include<linux/init.h>
#include<linux/module.h>
#include<linux/string.h>
#include<linux/blkdev.h>
#include<linux/hdreg.h>
#define SIMP_BLKDEV_MAJOR 180
#define SIMP_BLKDEV_DISKNAME "simp_blkdev"
#define SIMP_BLKDEV_BYTES (2*1024*1024) //块设备的大小 2M
static struct request_queue *simp_blkdev_queue = NULL; //良好的习惯
static struct gendisk *simp_blkdev_disk = NULL;
static unsigned char simp_blkdev_data[SIMP_BLKDEV_BYTES];
//负责处理块设备请求的函数
static void simp_blkdev_do_request(struct request_queue *q)
{
struct request *req;
req = blk_fetch_request(q);
//从请求队列中不断的提取下一条请求,然后对请求进行处理
while(req){
//如果传送的扇区超出块设备大小。
//扇区->字节,blk_rq_pos当前要传送的扇区,blk_rq_cur_sectors当前要传送的扇区数量
if((blk_rq_pos(req) + blk_rq_cur_sectors(req)) << 9 > SIMP_BLKDEV_BYTES){
printk(KERN_ERR "bad request:block=%llu,count=%u\n",(unsigned long long)blk_rq_pos(req),blk_rq_cur_sectors(req));
if(!__blk_end_request_cur(req,0))//结束这个请求,0表示成功
req = blk_fetch_request(q);
continue;
}
switch (rq_data_dir(req)){
case READ:
memcpy(req->buffer,simp_blkdev_data+(blk_rq_pos(req) << 9),blk_rq_cur_sectors(req) << 9); //从第二个参数的地址拷贝第三个参数的字节数到第一个参数的地址
if(!__blk_end_request_cur(req,0))//结束这个请求,0表示成功
req = blk_fetch_request(q);
break;
case WRITE:
memcpy(simp_blkdev_data+(blk_rq_pos(req) << 9),req->buffer,blk_rq_cur_sectors(req) << 9);
if(!__blk_end_request_cur(req,0))//结束这个请求,0表示成功
req = blk_fetch_request(q);
break;
default:
break;
}
}
}
struct block_device_operations simp_blkdev_fops = {
.owner = THIS_MODULE,
};
static int __init simp_blkdev_init(void)
{
int ret;
//请求队列初始化,绑定函数
simp_blkdev_queue = blk_init_queue(simp_blkdev_do_request,NULL);
if(!simp_blkdev_queue){ //初始化失败
ret = - ENOMEM;
goto err_init_queue;
}
register_blkdev(SIMP_BLKDEV_MAJOR,SIMP_BLKDEV_DISKNAME);//注册主设备
simp_blkdev_disk = alloc_disk(1); //申请gendisk
if(!simp_blkdev_disk){
ret = - ENOMEM;
goto err_alloc_disk;
}
//gendisk初始化
strcpy(simp_blkdev_disk->disk_name,SIMP_BLKDEV_DISKNAME);
simp_blkdev_disk->major = SIMP_BLKDEV_MAJOR;
simp_blkdev_disk->first_minor = 0; //第一次设备号
simp_blkdev_disk->fops = &simp_blkdev_fops;
simp_blkdev_disk->queue = simp_blkdev_queue;
//设置块设备的大小,块设备使用扇区作为单位设置,而扇区的大小默认是512字节。
//在把字节为单位的大小转换为以扇区为单位时,我们需要除以512,或者右移9位可能更快一些。
set_capacity(simp_blkdev_disk,SIMP_BLKDEV_BYTES>>9);
add_disk(simp_blkdev_disk); //向系统添加这个块设备
return 0;
err_alloc_disk:
blk_cleanup_queue(simp_blkdev_queue);
err_init_queue:
return ret;
}
static void __exit simp_blkdev_exit(void)
{
unregister_blkdev(SIMP_BLKDEV_MAJOR,SIMP_BLKDEV_DISKNAME);//注销设备驱动
del_gendisk(simp_blkdev_disk); //从系统中删除这个块设备
put_disk(simp_blkdev_disk); //释放申请的空间
blk_cleanup_queue(simp_blkdev_queue);
}
module_init(simp_blkdev_init);
module_exit(simp_blkdev_exit);
MODULE_AUTHOR("ZHYANG");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("simp_blkdev");
Makefile:
obj-m := simp_blkdev.o
KDIR := /opt/FriendlyARM/mini6410/linux/linux-2.6.38/
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
rm -rf *.ko *.o *.mod.* *.symvers *.order
测试(我是在tiny6410(2.6.38)上测试的,因为我的电脑是3.16的,虽然我又编译了2.6.38的内核但就是跑不起来,无奈就用板子试了)没有板子也没关系,只要电脑内核是2的也能成功:
1.insmod simp_blkdev.ko
2.mkfs.ext2 /dev/simp_blkdev
板子上只有ext2和vfat两种文件系统,没有ext3更没有ext4,所以我这边用ext2,查看支持的文件系统用ls /sbin/mkfs.*
3.mkdir -p /mnt/blk
4.mount /dev/simp_blkdev /mnt/blk
未mount前lsmod和mount后是这样的
可以看到模块引用计数从0变为1了,等会umount掉又会变成0
5.ls /mnt/blk
6.umount /mnt/blk
7.rmmod simp_blkdev
我的这份代码跟网上其他类似的代码不同的地方有这些:
1.最坑爹的一点!!!!blk_end_request_cur(req,0) 0是表示成功的!不是网上代码看到的0是失败,害我整整搞了一天啊!!!!!!!一直在mkfs.ext2那句整个板子卡死
2.网上其他代码的主设备号是抢其他的设备的,我这边是单独自己选个没用到的设备自己进行注册,所以才有init中才有register_blkdev这句,insmod成功之后你能在cat /proc/device中看到它
3.其他的一些变化详见上篇
4.__blk_end_request_cur或者__blk_end_request_all都可以用,效果是一样的,我还不知道具体区别是什么。
ps:
blk_alloc_queue分配的请求队列中make_request_fn是没有被赋值的,所以要实现无请求队列的simp_blkdev要自己写个simp_blkdev_make_request并绑定到request_quque的make_request_fn,而blk_init_quque则把make_request_fn与simp_blkdev_do_request绑定在一起。