本章的目的用尽可能最简单的方法写出一个能用的块设备驱动。.DOC

上传人:天*** 文档编号:1889121 上传时间:2019-03-20 格式:DOC 页数:94 大小:297.50KB
下载 相关 举报
本章的目的用尽可能最简单的方法写出一个能用的块设备驱动。.DOC_第1页
第1页 / 共94页
本章的目的用尽可能最简单的方法写出一个能用的块设备驱动。.DOC_第2页
第2页 / 共94页
本章的目的用尽可能最简单的方法写出一个能用的块设备驱动。.DOC_第3页
第3页 / 共94页
本章的目的用尽可能最简单的方法写出一个能用的块设备驱动。.DOC_第4页
第4页 / 共94页
本章的目的用尽可能最简单的方法写出一个能用的块设备驱动。.DOC_第5页
第5页 / 共94页
点击查看更多>>
资源描述

1、本章的目的用尽可能最简单的方法写出一个能用的块设备驱动。所谓的能用,是指我们可以对这个驱动生成的块设备进行 mkfs,mount 和读写文件。为了尽可能简单,这个驱动的规模不是 1000 行,也不是 500 行,而是 100 行以内。这里插一句,我们不打算在这里介绍如何写模块,理由是介绍的文章已经满天飞舞了。如果你能看得懂、并且成功地编译、运行了这段代码,我们认为你已经达到了本教程的入学资格,当然,如果你不幸的卡在这段代码中,那么请等到搞定它以后再往下看:mod.c:#include static int _init init_base(void)printk(“-Hello. World-n

2、“);return 0;static void _exit exit_base(void)printk(“-Bye-n“);module_init(init_base);module_exit(exit_base);MODULE_LICENSE (“GPL“);MODULE_AUTHOR(“Zhao Lei“);MODULE_DESCRIPTION(“For test“);Makefile:obj-m := mod.oKDIR := /lib/modules/$(shell uname -r)/buildPWD := $(shell pwd)default:$(MAKE) -C $(KDIR)

3、 SUBDIRS=$(PWD) modulesclean:$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) cleanrm -rf Module.markers modules.order Module.symvers好了,这里我们假定你已经搞定上面的最简单的模块了,懂得什么是看模块,以及简单模块的编写、编译、加载和卸载。还有就是,什么是块设备,什么是块设备驱动,这个也请自行 google 吧,因为我们已经迫不及待要写完程序下课。为了建立一个可用的块设备,我们需要做.1 件事情:1:用 add_disk()函数向系统中添加这个块设备添加一个全局的static struct g

4、endisk *simp_blkdev_disk;然后申明模块的入口和出口:module_init(simp_blkdev_init);module_exit(simp_blkdev_exit);然后在入口处添加这个设备、出口处私房这个设备:static int _init simp_blkdev_init(void)add_disk(simp_blkdev_disk);return 0;static void _exit simp_blkdev_exit(void)del_gendisk(simp_blkdev_disk);当然,在添加设备之前我们需要申请这个设备的资源,这用到了 alloc

5、_disk()函数,因此模块入口函数simp_blkdev_init(void)应该是:static int _init simp_blkdev_init(void)simp_blkdev_disk = alloc_disk(1);if (!simp_blkdev_disk) ret = -ENOMEM;goto err_alloc_disk;add_disk(simp_blkdev_disk);return 0;err_alloc_disk:return ret;还有别忘了在卸载模块的代码中也加一个行清理函数:put_disk(simp_blkdev_disk);还有就是,设备有关的属性也是

6、需要设置的,因此在 alloc_disk()和 add_disk()之间我们需要:strcpy(simp_blkdev_disk-disk_name, SIMP_BLKDEV_DISKNAME);simp_blkdev_disk-major = ?1;simp_blkdev_disk-first_minor = 0;simp_blkdev_disk-fops = ?2;simp_blkdev_disk-queue = ?3;set_capacity(simp_blkdev_disk, ?4);SIMP_BLKDEV_DISKNAME 其实是这个块设备的名称,为了绅士一些,我们把它定义成宏了:#

7、define SIMP_BLKDEV_DISKNAME “simp_blkdev“这里又引出了 4 个问号。(天哪,是不是有种受骗的感觉,像是陪老婆去做头发)第 1 个问号:每个设备需要对应的主、从驱动号。我们的设备当然也需要,但很明显我不是脑科医生,因此跟写 linux 的那帮疯子不熟,得不到预先为我保留的设备号。还有一种方法是使用动态分配的设备号,但在这一章中我们希望尽可能做得简单,因此也不采用这种方法。那么我们采用的是:抢别人的设备号。我们手头没有 AK47,因此不敢干的太轰轰烈烈,而偷偷摸摸的事情倒是可以考虑的。柿子要捡软的捏,而我们试图找出一个不怎么用得上的设备,然后抢他的 ID。打

8、开 linux/include/linux/major.h,把所有的设备一个个看下来,我们觉得最胜任被抢设备号的家伙非COMPAQ_SMART2_XXX 莫属。第一因为它不强势,基本不会被用到,因此也不会造成冲突;第二因为它有钱,从COMPAQ_SMART2_MAJOR 到 COMPAQ_SMART2_MAJOR7 有那 8 个之多的设备号可以被抢,不过瘾的话还有它妹 妹:COMPAQ_CISS_MAJORCOMPAQ_CISS_MAJOR7。为了让抢劫显得绅士一些,我们在外面又定义一个宏:#define SIMP_BLKDEV_DEVICEMAJOR COMPAQ_SMART2_MAJOR然

9、后在?1 的位置填上 SIMP_BLKDEV_DEVICEMAJOR。第 2 个问号:gendisk 结构需要设置 fops 指针,虽然我们用不到,但该设还是要设的。好吧,就设个空得给它:在全局部分添加:struct block_device_operations simp_blkdev_fops = .owner = THIS_MODULE,;然后把?2 的位置填上3:在加载模块时用 simp_blkdev_do_request()函数的地址作参数调用 blk_init_queue()初始化一个请求队列:simp_blkdev_queue = blk_init_queue(simp_blkd

10、ev_do_request, NULL);if (!simp_blkdev_queue) ret = -ENOMEM;goto err_init_queue;4:卸载模块时把 simp_blkdev_queue 还回去:blk_cleanup_queue(simp_blkdev_queue);5:在?3 的位置填上 simp_blkdev_queue。第 4 个问号:这个还好,比前面的简单多了,这里需要设置块设备的大小。块设备的大小使用扇区作为单位设置,而扇区的大小默认是 512 字节。当然,在把字节为单位的大小转换为以扇区为单位时,我们需要除以 512,或者右移 9 位可能更快一些。同样,我

11、们试图把这一步也做得绅士一些,因此使用宏定义了块设备的大小,目前我们定为 16M:#define SIMP_BLKDEV_BYTES (16*1024*1024)然后在?4 的位置填上 SIMP_BLKDEV_BYTES9。看到这里,是不是有种身陷茫茫大海的无助感?并且一波未平,一波又起,在搞定这 4 个问号的同时,居然又引入了 simp_blkdev_do_request 函数!当然,如果在身陷茫茫波涛中时你认为到处都是海,因此绝望,那么恭喜你可以不必挨到 65 岁再退休;反之,如果你认为到处都是没有三聚氰胺鲜鱼,并且随便哪个方向都是岸时,那么也恭喜你,你可以活着回来继续享受身为纳税人的荣誉

12、。为了理清思路,我们把目前为止涉及到的代码整理出来:#define SIMP_BLKDEV_DEVICEMAJOR COMPAQ_SMART2_MAJOR#define SIMP_BLKDEV_DISKNAME “simp_blkdev“#define SIMP_BLKDEV_BYTES (16*1024*1024)static struct request_queue *simp_blkdev_queue;static struct gendisk *simp_blkdev_disk;static void simp_blkdev_do_request(struct request_queu

13、e *q);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;simp_blkdev_disk = alloc_disk(1);if (!simp_blkdev_di

14、sk) ret = -ENOMEM;goto err_alloc_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_disk-queue = simp_blkdev_queue;set_capacity(simp_blkdev_disk, SIMP_BLKDEV_BYTES9);add_d

15、isk(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)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_exi

16、t);剩下部分的不多了,真的不多了。请相信我,因为我不在质监局上班。我写的文章诚实可靠,并且不拿你纳税的钱。我们还有一个最重要的函数需要实现,就是负责处理块设备请求的 simp_blkdev_do_request()。首先我们看看究竟把块设备的数据以什么方式放在内存中。毕竟这是在第 1 章,因此我们将使用最 simple 的方式实现,也就是,数组。我们在全局代码中定义:unsigned char simp_blkdev_dataSIMP_BLKDEV_BYTES;对驱动程序来说,这个数组看起来大了一些,如果不幸被懂行的人看到,将 100%遭到最无情、最严重的鄙视。而我们却从极少数公仆那里学到了

17、最有效的应对之策,那就是:无视他,然后把他定为成“不明真相的群众”。然后我们着手实现 simp_blkdev_do_request。这里介绍 elv_next_request()函数,原型是:struct request *elv_next_request(struct request_queue *q);用来从一个请求队列中拿出一条请求(其实严格来说,拿出的可能是请求中的一段) 。随后的处理请求本质上是根据 rq_data_dir(req)返回的该请求的方向( 读/ 写) ,把块设备中的数据装入 req-buffer、或是把 req-buffer 中的数据写入块设备。刚才已经提及了与 req

18、uest 结构相关的 rq_data_dir()宏和.buffer 成员,其他几个相关的结构成员和函数是:request.sector:请求的开始磁道request.current_nr_sectors:请求磁道数end_request():结束一个请求,第 2 个参数表示请求处理结果,成功时设定为 1,失败时设置为 0 或者错误号。因此我们的 simp_blkdev_do_request()函数为:static void simp_blkdev_do_request(struct request_queue *q)struct request *req;while (req = elv_ne

19、xt_request(q) != NULL) if (req-sector + req-current_nr_sectors) SIMP_BLKDEV_BYTES) printk(KERN_ERR SIMP_BLKDEV_DISKNAME“: bad request: block=%llu, count=%un“,(unsigned long long)req-sector,req-current_nr_sectors);end_request(req, 0);continue;switch (rq_data_dir(req) case READ:memcpy(req-buffer,simp_

20、blkdev_data + (req-sector current_nr_sectors sector buffer, req-current_nr_sectors elevator;if (IS_ERR_VALUE(elevator_init(simp_blkdev_queue, “noop“)printk(KERN_WARNING “Switch elevator failed, using defaultn“);elseelevator_exit(old_e);为方便阅读并提高本文在 google 磁盘中的占用率,我们给出修改后的整个 simp_blkdev_init()函数:static int _init simp_blkdev_init(void)int ret;elevator_t *old_e;simp_blkdev_queue = blk_init_queue(simp_blkdev_do_request, NULL);if (!simp_blkdev_queue) ret = -ENOMEM;goto err_init_queue;

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 重点行业资料库 > 1

Copyright © 2018-2021 Wenke99.com All rights reserved

工信部备案号浙ICP备20026746号-2  

公安局备案号:浙公网安备33038302330469号

本站为C2C交文档易平台,即用户上传的文档直接卖给下载用户,本站只是网络服务中间平台,所有原创文档下载所得归上传人所有,若您发现上传作品侵犯了您的权利,请立刻联系网站客服并提供证据,平台将在3个工作日内予以改正。