已阅读5页,还剩6页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
学写块设备驱动(一)-了解gendisk及request处理函数以下是一个最简单的块设备驱动,写完可以对编写块设备驱动的框架有初步了解。环境:Linux2.6.29源码:simp_blkdev.c:#include#include#include#include#include#defineSIMP_BLKDEV_DISKNAMEsimp_blkdev#defineSIMP_BLKDEV_DEVICEMAJORCOMPAQ_SMART2_MAJOR#defineSIMP_BLKDEV_BYTES(8*1024*1024)staticDEFINE_SPINLOCK(rq_lock);unsignedcharsimp_blkdev_dataSIMP_BLKDEV_BYTES;staticstructgendisk*simp_blkdev_disk;staticstructrequest_queue*simp_blkdev_queue;/devicesrequestqueuestructblock_device_operationssimp_blkdev_fops=.owner=THIS_MODULE,;/handlerequestthatpasstothisdevicestaticvoidsimp_blkdev_do_request(structrequest_queue*q)structrequest*req;while(req=elv_next_request(q)!=NULL)if(req-sector+req-current_nr_sectors)SIMP_BLKDEV_BYTES)printk(KERN_ERRSIMP_BLKDEV_DISKNAME:badrequest:block=%llu,count=%un,(unsignedlonglong)req-sector,req-current_nr_sectors);end_request(req,0);continue;switch(rq_data_dir(req)caseREAD:memcpy(req-buffer,(simp_blkdev_data+(req-sectorcurrent_nr_sectorssectorbuffer,req-current_nr_sectorsdisk_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;set_capacity(simp_blkdev_disk,SIMP_BLKDEV_BYTES9);add_disk(simp_blkdev_disk);printk(modulesimp_blkdevadded.n);return0;error_init_queue:blk_cleanup_queue(simp_blkdev_queue);error_alloc_disk:returnret;staticvoidsimp_blkdev_exit(void)del_gendisk(simp_blkdev_disk);put_disk(simp_blkdev_disk);blk_cleanup_queue(simp_blkdev_queue);printk(modulesimp_blkdevromoved.n);module_init(simp_blkdev_init);module_exit(simp_blkdev_exit);Makefile:obj-m:=simp_blkdev.oKDIR=/lib/modules/$(shelluname-r)/buildall:$(MAKE)-C$(KDIR)M=$(PWD).PHONY:cleanclean:rm-f*.mod.c*.mod.o*.ko*.o*.tmp_versions*.markers*.symvers*.order使用块设备:insmodsimp_blkdev.kolsmod|head可以看到simp_blkdev模块,used by列的值为0,说明未被使用mkfs.ext3/dev/simp_blkdevmount/dev/simp_blkdev/mnt/simp_blkdevlsmod|head此时初始化文件系统并挂载,used by列的值为1,说明被使用cp /etc/init.d/* /mnt/simp_blkdevls /mnt/simp_blkdev此时我们的块设备已经显得颇正常了, rm-rf/mnt/simp_blkdevumount/mnt/simp_blkdevlsmod|headsimp_blkdev模块,used by列的值重新为0收获:1.要让一个最简单的块设备驱动可用,必须实现的关键结构为gendisk和request_queue。gendisk结构描述一个磁盘,包括主从设备号、设备操作函数、容量等信息,它通过gendisk-queue和request_queue联系起来,request_queue初始化时又向内核块设备层注册了处理request的函数(该例子中为simp_blkdev_do_request)。2.该版本适用于2.6.29内核,从2.6.31内核开始,一些api发生变化(见linux/include/blkdev.h)。在2.6.32内核中,request-sectors变为blk_rq_pos(request)request - nr_sectors变为blk_rq_nr_sectors(request)elev_next_request(request)变为blk_fetch_request(request)end_request(request, error)变为blk_end_request_all(request, error)我在2.6.32内核下对照上述源码修改api时遇到一些问题。假如使用blk_end_request_all(request,error),insmod直接死机;假如使用_blk_end_request_all(request,error),可以insmod,但是不能够mkfs和mount,查看dmesg为“EXT3-no journal”错误。查看源码blk-core.c,只知道blk_end_request_all比_blk_end_request_all多了加锁和解锁的操作,但是由于队列锁方面的知识不足,现在无法解决该错误,故暂时使用2.6.29内核实践学习。如果读者你恰巧遇到同样的问题并恰巧成功了,请您一定告诉我答案。学写块设备驱动(二)-更换IO调度器上节我们的块设备驱动已经可以使用了,本节我们对其进行一点小的改动,修改其使用的IO调度器。我们知道,标准磁盘的寻道延时很高,故有了IO调度器存在的必要,它通过对IO请求进行合并或者排序来提高块设备的使用效率。但是因为我们目前的块设备在内存中,即没有通常的磁盘寻道延时,且读写迅速,所以我们不需要IO调度器为我们做多余的事情浪费资源。目前Linux有四种IO调度器,anticipatory、cfq、deadline和noop。例子这里最适合我们的是noop,它基本对IO请求什么都不做。我们想要更换IO调度器,关键的函数是这么两个:首先,为请求队列选择IO调度器int elevator_init(struct request_queue *q, char *name)其次,由于我们使用blk_init_queue(request_fn_proc *)初始化IO请求队列时,通用块层为我们初始化了默认的IO调度器,所以我们需要下面的函数来释放它:void elevator_exit(struct elevator_queue *old_eq) /释放某个请求队列上的IO调度器更改后的simp_blkdev_init 函数如下:staticintsimp_blkdev_init(void)intret;structelevator_queue*old_e;/inittherequestqueuebythehandlerfunction simp_blkdev_queue=blk_init_queue(simp_blkdev_do_request,&rq_lock);if(!simp_blkdev_queue)ret=-ENOMEM;gotoerror_init_queue;/initanewioscheduler old_e=simp_blkdev_queue-elevator;if(IS_ERR_VALUE(elevator_init(simp_blkdev_queue,noop)printk(KERN_WARNINGSwitchelevatorfailed,usingdefaultn);elseelevator_exit(old_e);/alloctheresourceofgendisk simp_blkdev_disk=alloc_disk(1);if(!simp_blkdev_disk)ret=-ENOMEM;gotoerror_alloc_disk;/populatethegendiskstructure 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;set_capacity(simp_blkdev_disk,SIMP_BLKDEV_BYTES9);add_disk(simp_blkdev_disk);printk(modulesimp_blkdevadded.n);return0;error_init_queue:blk_cleanup_queue(simp_blkdev_queue);error_alloc_disk:returnret;static int simp_blkdev_init(void)int ret;效果没有更换IO调度器之前(cfq): 更换IO调度器(noop):学写块设备驱动(三)-踢开IO调度器,自己处理bio(上)前两篇我们编写了在内存中的最简单的块设备驱动程序,并为其更换了我们心仪的noopIO调度器。本篇我们试着搞清楚内核的块设备层在这里为我们做的事情,以及我们如何做点自己想做的事情。其实,我们前面两篇都是围绕着请求队列(request_queue)这东西做事情。初始化请求队列时我们注册上驱动处理请求(request)的策略函数(simp_blkdev_do_request),然后在gendisk结构初始化时又填充上前面初始化好的queue。后面我们又用noopIO调度器更换掉默认的cfq调度器。下面试着搞清楚通用块层在这里的框架机制。先看一张图:当通用块层以上的层要对块设备进行访问时,通常是准备好一个bio,调用generic_make_request(struct bio *bio),OK。但是我们是编写底层驱动的可怜IT男,要是不知道generic_make_request是怎么把我们前面实现的simp_blkdev_do_request和request_queue联系起来,那就相当没有安全感。于是,我们开始RTFSC。既然是围着request_queue做文章,那么我们先看下这个数据结构:struct request_queue.request_fn_proc *request_fn;/ Method that implements the entry point of the strategy routine of the drivermake_request_fn *make_request_fn;/Method invoked when a new request has to be inserted in the queue.聪明的你肯定也看出来了,上面是两个函数指针,它们的定义如下:typedef void (request_fn_proc) (struct request_queue *q);typedef int (make_request_fn) (struct request_queue *q, struct bio *bio);对上面的数据结构留个印象,我们开始看通用块层的入口函数generic_make_request(struct bio *bio)。你可以发现下面的调用关系generic_make_request(struct bio *bio)-_generic_make_request-q-make_request_fn(q, bio)于是我们得出结论,generic_make_request()最终是通过调用request_queue.make_request_fn函数完成bio所描述的请求处理的。那么,make_request_fn具体又指向哪个函数呢?我们前面也没有实现过make_request_fn这样的函数啊?!我们只记得初始化request_queue时调用了blk_init_queue(request_fn_proc *, spinlock_t *)这个函数,所以我们来看一下这个函数,blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)-blk_init_queue_node()-q-unplug_fn=generic_unplug_device;q-request_fn=rfn;blk_queue_make_request(q,_make_reqeust)-q-make_request_fn=mfn(即_make_request)原来,我们request_queue的make_request_fn实际上指向了_make_request()函数。这样,大名鼎鼎的_make_request()函数就可以叫来某个IO调度器帮忙,并对bio做些利于用户的加工,比如将其映射到非线性映射区域。至此,我们知道了一个新的request是如何被提交给IO调度器的了(通过_make_request)。我们也知道了我们编写的simp_blkdev_do_request就是在这里被赋值给q-request_fn的了(通过blk_queue_make_request)。那么,通用块层的框架是什么时候对我们的simp_blkdev_do_request函数进行调用,从而真正执行对数据的拷贝了呢?这里有点复杂,先补充一点理论知识,就一点,块设备有“阻塞”和“非阻塞”的状态,从而通用块层才能利用这样的机制推迟对请求的处理,从而给了IO调度参与的机会;当“非阻塞”时(该函数为blk_remove_plug),才能够处理请求。有了这样的知识,我们RTFSC时见到blk_remove_plug就不奇怪了。好了,我们下面给出q-request_fn指向的simp_blkdev_do_request在何时被调用。这次从_make_request()开始,这函数果然强大。_make_request()-add_request()-_elv_add_request()-elv_insert()-blk_remove_plug-blk_unplug_timeout-kblocked_schedule_work-blk_unplug_work()-q-unplug_fn()如果你循到了上面的调用关系,那么恭喜你,你遇到了新问题,q-unplug_fn是哪只鸟,它调用的谁?如果你记性还不错,那么你会想起我们blk_init_queue调用关系里面有一个“q-unplug_fn=generic_unplug_device;”,很好,我们发现q-unplug_fn其实调用的是通用块层另一个小有名气的函数,generic_unplug_device()。我们继续寻找调用关系。generic_unplug_device()-_generic_unplug_device()-q-request_fn()到这里,长征结束,期间和IO调度器打个照面,见识了块设备解除阻塞状态,还唤醒了内核的工作队列,但最终我们可以大呼一口气了。综上所述,当我们实现了块设备驱动程序的策略函数(例如前面实现的simp_blkdev_do_request)并用其作为参数初始化一个request_queue后,通用块层的make_request_fn函数指针帮我们指定了强力帮手_make_request,该帮手又拉上了IO调度器,于是,一个bio经过通用块层、IO调度层的处理,最后以request_queue中的request喂给我们实现的策略函数。学写块设备驱动(三)-踢开IO调度器,自己处理bio(下)本篇的(上)基本搞清楚了我们已经实现的内存块设备驱动和通用块层之间的丝丝联系。现在我们该做点自己想做的事情了: 踢开IO调度器,自己来处理bio。踢开IO调度器很容易,即不使用_make_request 这个系统指定的强力函数,如何不使用?其实我们从(上)的blk_init_queue()函数中也能看出来,系统使用了blk_queue_make_request(q, _make_request)这个函数,那么我们也可以使用这个函数来指定我们自己的策略函数,从而替换掉_make_request函数。那初始化request_queue的blk_init_queue函数也不需要了。直接看更改过后的源码:simp_blkdev.c:#include #include #include #include #include #include #defineSIMP_BLKDEV_DISKNAMEsimp_blkdev #defineSIMP_BLKDEV_DEVICEMAJORCOMPAQ_SMART2_MAJOR #defineSIMP_BLKDEV_BYTES(8*1024*1024) staticDEFINE_SPINLOCK(rq_lock);unsignedcharsimp_blkdev_dataSIMP_BLKDEV_BYTES;staticstructgendisk*simp_blkdev_disk;staticstructrequest_queue*simp_blkdev_queue;/devicesrequestqueue structblock_device_operationssimp_blkdev_fops=.owner=THIS_MODULE,;/handlebio staticintsimp_blkdev_make_request(structrequest_queue*q,structbio*bio)structbio_vec*bvec;inti;void*dsk_mem;if(bio-bi_sectorbi_sizeSIMP_BLKDEV_BYTES)printk(KERN_ERRSIMP_BLKDEV_DISKNAME:badrequest:block=%llu,count=%un,(unsignedlonglong)bio-bi_sector,bio-bi_size);bio_endio(bio,-EIO);return0;dsk_mem=simp_blkdev_data+(bio-bi_sectorbv_page)+bvec-bv_offset;memcpy(iovec_mem,dsk_mem,bvec-bv_len);kunmap(bvec-bv_page);break;caseWRITE:iovec_mem=kmap(bvec-bv_page)+bvec-bv_offset;memcpy(dsk_mem,iovec_mem,bvec-bv_len);kunmap(bvec-bv_page);break;default:printk(KERN_ERRSIMP_BLKDEV_DISKNAME:unknownvalueofbio_rw:%lun,bio_rw(bio);bio_endio(bio,-EIO);return0;dsk_mem+=bvec-bv_len;bio_endio(bio,0);return0;staticintsimp_blkdev_init(void)intret;simp_blkdev_queue=blk_alloc_queue(GFP_KERNEL);if(!simp_blkdev_queue)ret=-ENOMEM;gotoerror_alloc_queue;blk_queue_make_request(simp_blkdev_queue,simp_blkdev_make_request);/alloctheresourceofgendisk simp_blkdev_disk=alloc_disk(1);if(!simp_blkdev_disk)ret=-ENOMEM;gotoerror_alloc_disk;/populatethegendiskstructurestrcpy(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;set_capacity(simp_blkdev_disk,SIMP_BLKDEV_BYTES9);add_disk(simp_blkdev_disk);printk(modulesimp_blkdevadded.n);return0;error_alloc_queue:blk_cleanup_queue(simp_blkdev_queue);error_alloc_disk:returnret;staticvoidsimp_blkdev_exit(void)del_gendisk(simp_blkdev_disk);put_disk(simp_blkdev_disk);blk_cleanup_queue(simp_blkdev_queue);printk(modulesimp_blkdevromoved.n);module_init(simp_blkdev_init);module_exit(simp_blkdev_exit);#include#include#include#include#include#include#define SIMP_BLKDEV_DISKNAME simp_blkdev#define SIMP_BLKDEV_DEVICEMAJOR COMPAQ_SMART2_MAJOR#define SIMP_BLKDEV_BYTES (8*1024*1024)static DEFINE_SPINLOCK(rq_lock);unsigned char simp_blkdev_dataSIMP_BLKDEV_BYTES;static struct gendisk *simp_blkdev_disk;static struct request_queue *simp_blkdev_queue;/devices request queuestruct block_device_operations simp_blkdev_fops = .owner = THIS_MODULE,;/handle biostatic int simp_blkdev_make_request(struct request_queue *q, struct bio *bio)struct bio_vec *bvec;int i;void *dsk_mem;if( (bio-bi_sector bi_size SIMP_BLKDEV_BYTES)printk(KERN_ERR SIMP_BLKDEV_DISKNAME :bad request:block=%llu,count=%un,(unsigned long long)bio-bi_sector,bio-bi_size);bio_endio(bio,-EIO);return 0;dsk_mem = simp_blkdev_data + (bio-bi_sector bv_page) + bvec-bv_offset;memcpy(iovec_mem, dsk_mem, bvec-bv_len);kunmap(bvec-bv_page);break;case WRITE:iovec_mem = kmap(bvec-bv_page) + bvec-bv_offset;memcpy(dsk_mem, iovec_mem, bvec-bv_len);kunmap(bvec-bv_page);break;default:printk(KERN_ERR SIMP_BLKDEV_DISKNAME : unknown value of bio_rw: %lun,bio_rw(bio);bio_endio(bio,-EIO);return 0;dsk_mem += bvec-bv_len;bio_endio(bio,0);return 0;static int simp_blkdev_init(void)int ret;simp_blkdev_queue = blk_alloc_queue(GFP_KERNEL);if(!simp_blkdev_queue)ret = -ENOMEM;goto error_alloc_queue;blk_queue_make_request(simp_blkdev_queue, simp_blkdev_make_request);/alloc the resource of gendisksimp_blkdev_disk = alloc_disk(1);if(!simp_blkdev_disk)ret = -ENOMEM;goto error_alloc_disk;/populate the gendisk structurestrcpy(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;set_capacity(simp_blkdev_disk,SIMP_BLKDEV_BYTES9);add_disk(simp_blkdev_disk);printk(module simp_blkdev added.n);return 0;error_alloc_queue:blk_cleanup_queue(simp_blkdev_queue);error_alloc_disk:return ret;static void simp_blkdev_exit(void)del_gendisk(simp_blkdev_disk);put_disk(simp_blkdev_disk);blk_cleanup_queue(simp_blkdev_queue);printk(module simp_blkdev romoved.n);module_init(simp_blkdev_init);module_exit(simp_blkdev_exit);为了不使用IO调度器,自己处理bio,我们需要掌握如下几个关键方法和数据结构: request_queue *blk_alloc_queue(gfp_t gfp_mask) /用来初始化request_queue,填充基本结构,如链表头,锁。void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn) /源码的注释对该函数做了清楚的说明:/*blk_queue_make_request-defineanalternatemake_requestfunctionforadevice*q:therequestqueueforthedevicetobeaffected*mfn:thealternatemake_requestfunction*Description:*Thenormalwayfor&structbiostobepassedtoadevice*driverisforthemtobecollectedintorequestsonarequest*queue,andthentoallowthedevicedri
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- GB/T 46453-2025食品追溯体系评价通则
- 2026年中国真空包装袋行业市场前景预测及投资价值评估分析报告
- 2026年中国伺服电动折弯机行业市场规模及投资前景预测分析报告
- 2025北京工业大学财务处劳务派遣用工人员招聘考试笔试参考题库附答案解析
- 2025年陕西广济堂医药集团股份有限公司招聘考试笔试参考题库附答案解析
- 2026福建省宁德市蕉城区消防大队招聘考试笔试模拟试题及答案解析
- 2025合肥工业大学管理学院智能系统与智能决策团队科研助理岗位招聘3人考试笔试备考试题及答案解析
- 合同变更2025年售后服务协议
- 2025年科研合作成果分配合同协议
- 中耳炎治疗护理方案
- 中建幕墙吊装方案
- 配电房巡视记录表
- 思想道德与法治2023版教学设计第二章 追求远大理想 坚定崇高信念
- SAP各模块常用表清单
- HP DL380 G9安装系统、划raid及通过ilo口安装系统
- 危险化学品包装物容器产品生产许可证实施细则
- 1世界法制史专题
- 锥齿轮加工工艺和夹具设计
- 电力系统分析智慧树知到答案章节测试2023年东北电力大学
- GB/T 41318-2022通风消声器
- 中小学生交通安全知识竞赛题库及答案
评论
0/150
提交评论