Linux 块设备驱动代码编写
按照ldd的说法,linux的设备驱动包括了char,block,net三种设备。char设备是比较简单的,只要分配了major、minor号,就可以进行读写处理了。相对而言,block和net要稍微复杂些。net设备姑且按下不谈,我们在以后的博文中会有涉及。今天,我们可以看看一个简单的block是怎么设计的。
为了将block和fs分开,kernel的设计者定义了requestqueue这一种形式。换一句话说,所有fs对block设备的请求,最终都会转变为request的形式。所以,对于block设备驱动开发的朋友来说,处理好了requestqueue就掌握了block设备的一半。当然,block设备很多,hd、floppy、ram都可以这么来定义,有兴趣的朋友可以在drivers/block寻找相关的代码来阅读。兴趣没有那么强的同学,可以看看我们这篇博文,基本上也能学个大概。有个基本的概念,再加上一个简单浅显的范例,对于一般的朋友来说,已经足够了。
闲话不多说,我们看看一个ramdisk代码驱动是怎么写的,代码来自《深入linux设备驱动程序内核机制》,
#include#include #include #include #include #include #include #include #include #defineRAMHD_NAME"ramhd" #defineRAMHD_MAX_DEVICE2 #defineRAMHD_MAX_PARTITIONS4 #defineRAMHD_SECTOR_SIZE512 #defineRAMHD_SECTORS16 #defineRAMHD_HEADS4 #defineRAMHD_CYLINDERS256 #defineRAMHD_SECTOR_TOTAL(RAMHD_SECTORS*RAMHD_HEADS*RAMHD_CYLINDERS) #defineRAMHD_SIZE(RAMHD_SECTOR_SIZE*RAMHD_SECTOR_TOTAL)//8mb typedefstruct{ unsignedchar*data; structrequest_queue*queue; structgendisk*gd; }RAMHD_DEV; staticchar*sdisk[RAMHD_MAX_DEVICE]={NULL}; staticRAMHD_DEV*rdev[RAMHD_MAX_DEVICE]={NULL}; staticdev_tramhd_major; staticintramhd_space_init(void) { inti; interr=0; for(i=0;i bi_bdev; RAMHD_DEV*pdev=bdev->bd_disk->private_data; if(((bio->bi_sector*RAMHD_SECTOR_SIZE)+bio->bi_size)>RAMHD_SIZE){ err=-EIO; returnerr; } pRHdata=pdev->data+(bio->bi_sector*RAMHD_SECTOR_SIZE); bio_for_each_segment(bvec,bio,i){ pBuffer=kmap(bvec->bv_page)+bvec->bv_offset; switch(bio_data_dir(bio)){ caseREAD: memcpy(pBuffer,pRHdata,bvec->bv_len); flush_dcache_page(bvec->bv_page); break; caseWRITE: flush_dcache_page(bvec->bv_page); memcpy(pRHdata,pBuffer,bvec->bv_len); break; default: kunmap(bvec->bv_page); gotoout; } kunmap(bvec->bv_page); pRHdata+=bvec->bv_len; } out: bio_endio(bio,err); return0; } staticintalloc_ramdev(void) { inti; for(i=0;i data=sdisk[i]; rdev[i]->queue=blk_alloc_queue(GFP_KERNEL); blk_queue_make_request(rdev[i]->queue,ramhd_make_request); rdev[i]->gd=alloc_disk(RAMHD_MAX_PARTITIONS); rdev[i]->gd->major=ramhd_major; rdev[i]->gd->first_minor=i*RAMHD_MAX_PARTITIONS; rdev[i]->gd->fops=&ramhd_fops; rdev[i]->gd->queue=rdev[i]->queue; rdev[i]->gd->private_data=rdev[i]; sprintf(rdev[i]->gd->disk_name,"ramhd%c",'a'+i); rdev[i]->gd->flags|=GENHD_FL_SUPPRESS_PARTITION_INFO; set_capacity(rdev[i]->gd,RAMHD_SECTOR_TOTAL); add_disk(rdev[i]->gd); } return0; } staticvoid__exitramhd_exit(void) { inti; for(i=0;i gd); put_disk(rdev[i]->gd); blk_cleanup_queue(rdev[i]->queue); } clean_ramdev(); ramhd_space_clean(); unregister_blkdev(ramhd_major,RAMHD_NAME); } module_init(ramhd_init); module_exit(ramhd_exit); MODULE_AUTHOR("dennis__chen@AMDLinuxFGL"); MODULE_DESCRIPTION("Theramdiskimplementationwithrequestfunction"); MODULE_LICENSE("GPL");
为了大家方便,顺便也把Makefile放出来,看过前面blog的朋友都知道,这其实很简单,
ifneq($(KERNELRELEASE),) obj-m:=ramdisk.o else PWD:=$(shellpwd) KVER:=$(shelluname-r) KDIR:=/lib/modules/$(KVER)/build all: $(MAKE)-C$(KDIR)M=$(PWD)modules clean: rm-rf.*.cmd*.o*.mod.c*.ko.tmp_versionsmodules.*Module.* endif
这段代码究竟有没有用呢?可以按照下面的步骤来做,
a)make一下,生成ramdisk.ko;
b)编译好了之后,就可以安装驱动了,在linux下是这么做的,sudoinsmodramdisk.ko;
c)安装好了,利用ls/dev/ramhd*,就会发现在/dev下新增两个结点,即/dev/ramhda和/dev/ramhdb;
d)不妨选择其中一个节点进行分区处理,sudofdisk/dev/ramhda,简单处理的话就建立一个分区,生成/dev/ramhda1;
e)创建文件系统,sudomkfs.ext3/dev/ramhda1;
f)有了上面的文件系统,就可以进行mount处理,不妨sudomount/dev/ramhda1/mnt;
g)上面都弄好了,大家就可以copy、delete文件试试了,是不是很简单。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。