Linux块设备驱动实例

来源:互联网 发布:还珠格格小燕子知乎 编辑:程序博客网 时间:2024/05/29 02:50

在上一篇文章中详细讲解了块设备驱动的相关知识,并有一些参考代码,但是由于linux系统版本的原因,在2.6.35.6版本中,编译有错误,故在这篇文章中,我们贴出了2.6.35.6版本下的块设备驱动的一个简单例子,代码如下所示。

#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <asm/uaccess.h>#include <linux/spinlock.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/hdreg.h>#include <linux/genhd.h>#include <linux/blkdev.h>#include <linux/slab.h>#define BLK_MAJOR 250#define BLK_NAME "myblkdev"#define DISK_SECTOR_SIZE 512#define DISK_BYTES (1024*1024*64)int blk_major=0;struct myblk_dev{int size;/*以字节为单位,设备大小*/unsigned char *data;/*数据数组*/short users;/*用户数目*/struct request_queue *queue;/*设备请求队列*/struct gendisk *gd;/*gendisk结构*/spinlock_t lock;/*用于互斥*/};struct myblk_dev *myblkdev;/*块设备请求处理函数*/static int myblk_make_request(struct request_queue *q,struct bio *bio){int i;char *mem_pbuf;char *disk_pbuf;struct myblk_dev *pmyblkdev;struct bio_vec *pbvec;if((bio->bi_sector*DISK_SECTOR_SIZE + bio->bi_size) > DISK_BYTES){bio_io_error(bio);return 0;}pmyblkdev = (struct myblk_dev*)bio->bi_bdev->bd_disk->private_data; /*得到设备结构体*///printk("myblk_make_request\n");disk_pbuf = pmyblkdev->data+bio->bi_sector*DISK_SECTOR_SIZE;/*得到要读写的起始位置*//*开始遍历这个bio中的每个bio_vec*/bio_for_each_segment(pbvec,bio,i){/*循环分散的内存segment*/mem_pbuf = kmap(pbvec->bv_page)+pbvec->bv_offset;/*获得实际内存地址*/switch(bio_data_dir(bio)){/*读写*/case READA:case READ:memcpy(mem_pbuf, disk_pbuf, pbvec->bv_len);break;case WRITE:memcpy(disk_pbuf, mem_pbuf, pbvec->bv_len);break;default:kunmap(pbvec->bv_page);bio_io_error(bio);return 0;}kunmap(pbvec->bv_page);/*清除映射*/disk_pbuf += pbvec->bv_len;}bio_endio(bio,0);return 0;}static int blk_ioctl(struct block_device *dev, fmode_t no, unsigned cmd, unsigned long arg){printk("#############\n");return -ENOTTY;}static int blk_open(struct block_device *dev, fmode_t no){struct myblk_dev *device = dev->bd_inode->i_bdev->bd_disk->private_data;spin_lock(&device->lock);device->users ++;spin_unlock(&device->lock);printk("blk mount succeed\n");return 0;}static int blk_release(struct gendisk *gd, fmode_t no){struct myblk_dev *device = gd->private_data;spin_lock(&device->lock);device->users --;spin_unlock(&device->lock);printk("blk umount succeed\n");return 0;}struct block_device_operations blk_ops={.owner = THIS_MODULE,.open = blk_open,.release = blk_release,.ioctl = blk_ioctl,};static int __init myblkdev_init(void){blk_major = register_blkdev(blk_major,BLK_NAME);/*注册驱动*//* register_blkdev注册函数注册失败时,返回一个负值,如果主设备号参数为0,表示动态分配,分配成功返回主设备号,如果主设备号参数非0,表示静态分配,分配成功返回0*/if(blk_major < 0){printk("unable to get major number\n");return -EBUSY;}else if(blk_major == 0){blk_major = BLK_MAJOR;printk("regiser blk dev succeed\n");}myblkdev = kmalloc (sizeof(struct myblk_dev), GFP_KERNEL);if(myblkdev == NULL){unregister_blkdev(blk_major,BLK_NAME);return -ENOMEM;}memset(myblkdev, 0, sizeof(struct myblk_dev));myblkdev->size = DISK_BYTES;/*设备的大小*/myblkdev->data = vmalloc(DISK_BYTES);/*设备的数据空间*/if(myblkdev->data == NULL){printk(KERN_NOTICE "vmalloc failure.\n");return -EBUSY;}spin_lock_init(&myblkdev->lock);myblkdev->queue = blk_alloc_queue(GFP_KERNEL);/*生成队列*/if(myblkdev->queue == NULL){printk("blk_alloc_queue failure\n");goto out_vfree;}blk_queue_make_request(myblkdev->queue,myblk_make_request);/*注册make_request,绑定请求制造函数*/blk_queue_logical_block_size(myblkdev->queue, DISK_SECTOR_SIZE);/*告知内核块设备硬件扇区的大小*/myblkdev->queue->queuedata = myblkdev;/*queuedata是void *指针,用来指向所需要的数据*//*分配gendisk,参数是这个磁盘使用的次设备号的数量,一般也就是磁盘分区的数量,此后minors不能被修改,数量1表示不包含分区*/myblkdev->gd = alloc_disk(1);if(! myblkdev->gd){printk("alloc_disk failure\n");goto out_vfree;}myblkdev->gd->major = blk_major;/*主设备号*/myblkdev->gd->first_minor = 0;/*第一个设备号*/myblkdev->gd->fops = & blk_ops;/*块文件结构变量*/myblkdev->gd->queue = myblkdev->queue;/*请求队列*/myblkdev->gd->private_data = myblkdev;/*私有数据指针*/snprintf(myblkdev->gd->disk_name,32,"myblkdev%c",'a');/*名字*/set_capacity(myblkdev->gd,DISK_BYTES>>9);/*设置gendisk容量*//*增加gendisk,gendisk结构体被分配之后,系统还不能使用这个磁盘,需要调用如下函数来注册这个磁盘设备*/add_disk(myblkdev->gd);printk("gendisk init success\n");return 0;out_vfree:if(myblkdev->data){vfree(myblkdev->data);}return 0;}static void __exit myblkdev_exit(void){/*清除请求队列*/blk_cleanup_queue(myblkdev->gd->queue);/*释放gendisk,当不再需要一个磁盘时,应当使用如下函数释放gendisk*/del_gendisk(myblkdev->gd);put_disk(myblkdev->gd);if(myblkdev->data){vfree(myblkdev->data);}unregister_blkdev(blk_major,BLK_NAME);kfree(myblkdev);printk("blk dev exit success!\n");}module_init(myblkdev_init);module_exit(myblkdev_exit);MODULE_AUTHOR("Fang Xieyun");MODULE_LICENSE("GPL");

在代码中,各函数的基本功能在代码中已经基本说明了。其中需要注意的是,alloc_disk函数,其函数原型如下:

struct gendisk *alloc_disk(int minors)
其功能是分配gendisk,参数minors是这个磁盘使用的次设备号的数量,一般也就是磁盘分区的数量,此后minors不能被修改,当这个参数为1时,表示不包含分区,也就是不能对这个磁盘进行分区。如下图所示,参数为1时,进行分区就会报错。



而当我们修改参数为2时,就表示有两个分区,可以对这个磁盘进行分区,分区结果如下所示。


其中我们的Makefile文件内容如下所示:

obj-m := myblkdev.oKERNELDIR := /lib/modules/$(shell uname -r)/build/PWD:=$(shell pwd)default:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesclean:$(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) cleaninstall:insmod myblkdev.kouninstall:rmmod myblkdev


原创粉丝点击