原理指导:我们通过vmalloc在内存中开辟一部分空间,作为一个虚拟的磁盘,然后我们以块设备的方式来访问这片内存,例如这个sbull模型。
sbull(Simple Block Utility for Loading Localities),该驱动程序实现了一个使用系统内存的块设备,从本质上讲,属于一种 RAM 磁盘驱动程序。字符设备的IO操作则是直接不绕弯的,块设备的IO操作会配对和整合。驱动的任务是处理请求,对于这些请求的排队和整合的工作有IO调度算法处理。所以块设备的核心工作就是:请求处理函数或者是制造请求。Block_devices_operations结构体中没有读写一类的成员函数,而只是包含打开、释放和IO控制等函数。
块设备的流程:
(1)先把模块模型搭建好
MODULE_LICENSE("Dual BSD/GPL");
static struct block_device_operations sbull_ops = {
.owner = THIS_MODULE,
.open = sbull_open,
.release = sbull_release,
.media_changed = sbull_media_changed,
.revalidate_disk = sbull_revalidate,
.ioctl = sbull_ioctl,
.getgeo = sbull_getgeo,
};
module_init(sbull_init);
module_exit(sbull_exit);
(2)定义一个我们用内存虚拟出来的块设备sbull_dev
struct sbull_dev {
int size; /* Device size in sectors */
u8 *data; /* The data array */
short users; /* How many users */
short media_change; /* Flag a media change? */
spinlock_t lock; /* For mutual exclusion */
struct request_queue *queue; /* The device request queue */
struct gendisk *gd; /* The gendisk structure */
struct timer_list timer; /* For simulated media changes */
};
这个设备结构体是我们工作的核心,也许你不知道需要哪些成员,不要紧,还是那句话,编写驱动的时候,需要设备表现出那些性质和功能,相应的添加上就OK了。
(3)设备的初始化
sbull_init( ){
一、sbull_major = register_blkdev(sbull_major, "sbull");
二、Static struct sbull_dev *Devices = kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL);//在这里可以对Kmalloc的结果判断下,然后进一步处理...
三、setup_device()跳到我们设备注册函数中去
}//初始化函数结束
(4)开始注册我们的这个设备
setup_device( ){ //安装(注册)一个设备前,一定要保证它被初始化了
这些成员都得出初始化好:
一、 int size; /* Device size in sectors */
u8 *data; /* The data array */
short users; /* How many users */
spinlock_t lock; /* For mutual exclusion */
struct request_queue *queue; /* The device request queue */
struct gendisk *gd; /* The gendisk structure */
二、add_disk(dev->gd);//这个重要的注册函数 }//函数结束
(5)扇区大小、data数组、user、锁lock的初始化:
一、dev->size = nsectors*hardsect_size;
dev->data = vmalloc(dev->size);
spin_lock_init(&dev->lock);
二、//dev->queue = blk_alloc_queue(GFP_KERNEL); //RM_NOQUEUE
//dev->queue = blk_init_queue(sbull_full_request, &dev->lock); //RM_FULL
dev->queue = blk_init_queue(sbull_request, &dev->lock); //RM_SIMPLE
(6)告知内核硬件扇区尺寸、和timer模拟的初始化
blk_queue_logical_block_size(dev->queue, hardsect_size);
init_timer(&dev->timer);
dev->timer.data = (unsigned long) dev;
dev->timer.function = sbull_invalidate;gendisk初始化
一、初始化gendisk:
dev->gd = alloc_disk(SBULL_MINORS);
二、 初始化gendisk的成员
dev->gd->major = sbull_major;
dev->gd->first_minor = which*SBULL_MINORS;
dev->gd->fops = &sbull_ops;
dev->gd->queue = dev->queue;
dev->gd->private_data = dev;
(7)设置gendisk容量为xxx_size个扇区大小
set_capacity(dev->gd , nsectors*(hardsect_size / KERNEL_SECTOR_SIZE));
(8)剩下的就是为devices_operation结构体里声明的那些接口进行实现:
.open = sbull_open,
.release = sbull_release,
.media_changed = sbull_media_changed,
.revalidate_disk = sbull_revalidate,
.ioctl = sbull_ioctl,
.getgeo = sbull_getgeo,
一、sbull_open(struct block_device *bdev,fmode_t mode )
二、sbull_release(struct gendisk *bd_disk, fmode_t mode)
三、sbull_media_changed(struct gendisk *gd)
四、sbull_revalidate(struct gendisk *gd)
五、sbull_invalidate(unsigned long ldev)
六、sbull_ioctl (struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
七、sbull_getgeo(struct block_device *bdev, struct hd_geometry *geo)
以下是代码:
- /*
- * Sample disk driver for 2.6.35.
- */
- //#include <linux/autoconf.h>
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- #include <linux/init.h>
- #include <linux/sched.h>
- #include <linux/kernel.h> /* printk() */
- #include <linux/slab.h> /* kmalloc() */
- #include <linux/fs.h> /* everything... */
- #include <linux/errno.h> /* error codes */
- #include <linux/timer.h>
- #include <linux/types.h> /* size_t */
- #include <linux/fcntl.h> /* O_ACCMODE */
- #include <linux/hdreg.h> /* HDIO_GETGEO */
- #include <linux/kdev_t.h>
- #include <linux/vmalloc.h>
- #include <linux/genhd.h>
- #include <linux/blkdev.h>
- #include <linux/buffer_head.h> /* invalidate_bdev */
- #include <linux/bio.h>
- MODULE_LICENSE("Dual BSD/GPL");
- static int sbull_major = 0;
- module_param(sbull_major, int, 0);
- static int hardsect_size = 512;
- module_param(hardsect_size, int, 0);
- static int nsectors = 25600; /* How big the drive is */
- module_param(nsectors, int, 0);
- static int ndevices = 1;
- module_param(ndevices, int, 0);
- /*
- * The different "request modes" we can use.
- */
- enum {
- RM_SIMPLE = 0, /* The extra-simple request function */
- RM_FULL = 1, /* The full-blown version */
- RM_NOQUEUE = 2, /* Use make_request */
- };
- //static int request_mode = RM_FULL;
- //static int request_mode = RM_SIMPLE;
- static int request_mode = RM_NOQUEUE;
- module_param(request_mode, int, 0);
- /*
- * Minor number and partition management.
- */
- #define SBULL_MINORS 16
- #define MINOR_SHIFT 4
- #define DEVNUM(kdevnum) (MINOR(kdev_t_to_nr(kdevnum)) >> MINOR_SHIFT
- /*
- * We can tweak our hardware sector size, but the kernel talks to us
- * in terms of small sectors, always.
- */
- #define KERNEL_SECTOR_SIZE 512
- /*
- * After this much idle time, the driver will simulate a media change.
- */
- #define INVALIDATE_DELAY 60*HZ
- /*
- * The internal representation of our device.
- */
- struct sbull_dev {
- int size; /* Device size in sectors */
- u8 *data; /* The data array */
- short users; /* How many users */
- short media_change; /* Flag a media change? */
- spinlock_t lock; /* For mutual exclusion */
- struct request_queue *queue; /* The device request queue */
- struct gendisk *gd; /* The gendisk structure */
- struct timer_list timer; /* For simulated media changes */
- };
- static struct sbull_dev *Devices = NULL;
- /*
- * Handle an I/O request.
- */
- static void sbull_transfer(struct sbull_dev *dev, unsigned long sector,
- unsigned long nsect, char *buffer, int write)
- {
- unsigned long offset = sector*KERNEL_SECTOR_SIZE;
- unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE;
- //printk("<0>""in %s offset=%d nbytes=%d write=%d\n",__FUNCTION__,offset,nbytes,write);
- //buffer[10]='\0';
- //printk(buffer);
- //printk("\n");
- if ((offset + nbytes) > dev->size) {
- printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
- return;
- }
- if (write)
- memcpy(dev->data + offset, buffer, nbytes);
- else
- memcpy(buffer, dev->data + offset, nbytes);
- }
- /*The simple form of the request function.*/
- static void sbull_request(struct request_queue *q)
- {
- struct request *req;
- req = blk_fetch_request(q);
- while (req != NULL) {
- struct sbull_dev *dev = req->rq_disk->private_data;
- if (! blk_fs_request(req)) { //在有些内核并不支持该函数
- printk (KERN_NOTICE "Skip non-fs request\n");
- __blk_end_request_all(req, -EIO);
- continue;
- }
- // printk (KERN_NOTICE "Req dev %d dir %ld sec %ld, nr %d f %lx\n",
- // dev - Devices, rq_data_dir(req),
- // req->sector, req->current_nr_sectors,
- // req->flags);
- // printk("sectors=%d\n",req->current_nr_sectors);
- sbull_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req),
- req->buffer, rq_data_dir(req));
- if ( ! __blk_end_request_cur(req, 0) ) {
- req = NULL;
- }
- }
- }
- /*
- * Transfer a single BIO.
- */
- static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio)
- {
- int i;
- struct bio_vec *bvec;
- sector_t sector = bio->bi_sector;
- /* Do each segment independently. */
- bio_for_each_segment(bvec, bio, i) {
- char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
- sbull_transfer(dev, sector, bio_cur_bytes(bio)>>9 ,
- buffer, bio_data_dir(bio) == WRITE);
- sector += bio_cur_bytes(bio)>>9;
- __bio_kunmap_atomic(bio, KM_USER0);
- }
- return 0; /* Always "succeed" */
- }
- /*
- * Transfer a full request.
- */
- static int sbull_xfer_request(struct sbull_dev *dev, struct request *req)
- {
- struct bio *bio;
- int nsect = 0;
- __rq_for_each_bio(bio, req) {
- sbull_xfer_bio(dev, bio);
- nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
- }
- return nsect;
- }
- /*
- * Smarter request function that "handles clustering".*/
- static void sbull_full_request(struct request_queue *q)
- {
- struct request *req;
- int nsect;
- struct sbull_dev *dev ;
- req = blk_fetch_request(q);
- dev = req->rq_disk->private_data;
- while (req != NULL) {
- if (! blk_fs_request(req)) {
- printk (KERN_NOTICE "Skip non-fs request\n");
- __blk_end_request_all(req, -EIO);
- continue;
- }
- // printk (KERN_NOTICE "Req dev %d dir %ld sec %ld, nr %d f %lx\n",
- // dev - Devices, rq_data_dir(req),
- // req->sector, req->current_nr_sectors,
- // req->flags);
- // printk("sectors=%d\n",req->current_nr_sectors);
- nsect = sbull_xfer_request(dev, req);
- __blk_end_request(req, 0, (nsect<<9));
- // req = blk_fetch_request(q);
- req = NULL;
- }
- }
- //The direct make request version
- static int sbull_make_request(struct request_queue *q, struct bio *bio)
- {
- struct sbull_dev *dev = q->queuedata;
- int status;
- status = sbull_xfer_bio(dev, bio);
- //bio_endio(bio, bio->bi_size, status);
- bio_endio(bio, status);
- return 0;
- }
- /*
- * Open and close.
- */
- static int sbull_open(struct block_device *bdev,fmode_t mode )
- {
- struct sbull_dev *dev = bdev->bd_disk->private_data;
- //printk("<0>" "fdfjdlksjfdlkj\n");
- del_timer_sync(&dev->timer);
- spin_lock(&dev->lock);
- if (! dev->users)
- check_disk_change(bdev);
- dev->users++;
- spin_unlock(&dev->lock);
- return 0;
- }
- static int sbull_release(struct gendisk *bd_disk, fmode_t mode)
- {
- struct sbull_dev *dev = bd_disk->private_data;
- spin_lock(&dev->lock);
- dev->users--;
- if (!dev->users) {
- dev->timer.expires = jiffies + INVALIDATE_DELAY;
- add_timer(&dev->timer);
- }
- spin_unlock(&dev->lock);
- return 0;
- }
- /*
- * Look for a (simulated) media change.
- */
- int sbull_media_changed(struct gendisk *gd)
- {
- struct sbull_dev *dev = gd->private_data;
- return dev->media_change;
- }
- /*
- * Revalidate. WE DO NOT TAKE THE LOCK HERE, for fear of deadlocking
- * with open. That needs to be reevaluated.
- */
- int sbull_revalidate(struct gendisk *gd)
- {
- struct sbull_dev *dev = gd->private_data;
- if (dev->media_change) {
- dev->media_change = 0;
- memset (dev->data, 0, dev->size);
- }
- return 0;
- }
- /*
- * The "invalidate" function runs out of the device timer; it sets
- * a flag to simulate the removal of the media.
- */
- void sbull_invalidate(unsigned long ldev)
- {
- struct sbull_dev *dev = (struct sbull_dev *) ldev;
- spin_lock(&dev->lock);
- if (dev->users || !dev->data)
- printk (KERN_WARNING "sbull: timer sanity check failed\n");
- else
- dev->media_change = 1;
- spin_unlock(&dev->lock);
- }
- /*
- * The ioctl() implementation
- */
- int sbull_ioctl (struct block_device *bdev, fmode_t mode,
- unsigned int cmd, unsigned long arg)
- {
- return 0;
- }
- static int sbull_getgeo(struct block_device *bdev, struct hd_geometry *geo)
- {
- unsigned long size;
- struct sbull_dev *pdev = bdev->bd_disk->private_data;
- size = pdev->size;
- geo->cylinders = (size & ~0x3f) >> 6;
- geo->heads = 4;
- geo->sectors = 16;
- geo->start = 0;
- return 0;
- }
- /*
- * The device operations structure.
- */
- static struct block_device_operations sbull_ops = {
- .owner = THIS_MODULE,
- .open = sbull_open,
- .release = sbull_release,
- .media_changed = sbull_media_changed,
- .revalidate_disk = sbull_revalidate,
- .ioctl = sbull_ioctl,
- .getgeo = sbull_getgeo,
- };
- /*
- * Set up our internal device.
- */
- static void setup_device(struct sbull_dev *dev, int which)
- {
- /*
- * Get some memory.
- */
- memset (dev, 0, sizeof (struct sbull_dev));
- dev->size = nsectors*hardsect_size;
- dev->data = vmalloc(dev->size);
- if (dev->data == NULL) {
- printk (KERN_NOTICE "vmalloc failure.\n");
- return;
- }
- spin_lock_init(&dev->lock);
- /*
- * The timer which "invalidates" the device.
- */
- init_timer(&dev->timer);
- dev->timer.data = (unsigned long) dev;
- dev->timer.function = sbull_invalidate;
- /*
- * The I/O queue, depending on whether we are using our own
- * make_request function or not.
- */
- switch (request_mode) {
- case RM_NOQUEUE:
- dev->queue = blk_alloc_queue(GFP_KERNEL);
- if (dev->queue == NULL)
- goto out_vfree;
- blk_queue_make_request(dev->queue, sbull_make_request);
- break;
- case RM_FULL:
- dev->queue = blk_init_queue(sbull_full_request, &dev->lock);
- if (dev->queue == NULL)
- goto out_vfree;
- break;
- default:
- printk(KERN_NOTICE "Bad request mode %d, using simple\n", request_mode);
- /* fall into.. */
- case RM_SIMPLE:
- dev->queue = blk_init_queue(sbull_request, &dev->lock);
- if (dev->queue == NULL)
- goto out_vfree;
- break;
- }
- // blk_queue_hardsect_size(dev->queue, hardsect_size);
- blk_queue_logical_block_size(dev->queue, hardsect_size);
- dev->queue->queuedata = dev;
- /*
- * And the gendisk structure.
- */
- dev->gd = alloc_disk(SBULL_MINORS);
- if (! dev->gd) {
- printk (KERN_NOTICE "alloc_disk failure\n");
- goto out_vfree;
- }
- dev->gd->major = sbull_major;
- dev->gd->first_minor = which*SBULL_MINORS;
- dev->gd->fops = &sbull_ops;
- dev->gd->queue = dev->queue;
- dev->gd->private_data = dev;
- snprintf (dev->gd->disk_name, 32, "sbull%c", which + 'a');
- set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
- add_disk(dev->gd);
- return;
- out_vfree:
- if (dev->data)
- vfree(dev->data);
- }
- static int __init sbull_init(void)
- {
- int i;
- /*
- * Get registered.
- */
- // printk("<0>" "add by lht\n");
- sbull_major = register_blkdev(sbull_major, "sbull");
- if (sbull_major <= 0) {
- printk(KERN_WARNING "sbull: unable to get major number\n");
- return -EBUSY;
- }
- /*
- * Allocate the device array, and initialize each one.
- */
- Devices = kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL);
- if (Devices == NULL)
- goto out_unregister;
- for (i = 0; i < ndevices; i++)
- setup_device(Devices + i, i);
- return 0;
- out_unregister:
- unregister_blkdev(sbull_major, "sbd");
- return -ENOMEM;
- }
- static void sbull_exit(void)
- {
- int i;
- for (i = 0; i < ndevices; i++) {
- struct sbull_dev *dev = Devices + i;
- del_timer_sync(&dev->timer);
- if (dev->gd) {
- del_gendisk(dev->gd);
- put_disk(dev->gd);
- }
- if (dev->queue) {
- if (request_mode == RM_NOQUEUE)
- // blk_put_queue(dev->queue);
- kobject_put(&(dev->queue)->kobj);
- else
- blk_cleanup_queue(dev->queue);
- }
- if (dev->data)
- vfree(dev->data);
- }
- unregister_blkdev(sbull_major, "sbull");
- kfree(Devices);
- }
- module_init(sbull_init);
- module_exit(sbull_exit);