注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

gmd20的个人空间

// 编程和生活

 
 
 

日志

 
 

虚拟光驱学习(三) Linux上面最简单的block device  

2008-12-10 17:29:23|  分类: 程序设计 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

Linux Device Drivers 3rd edition.Oreilly.chm 一书所提供的一个简单block device例子,sbull。它在系统上虚拟了几个简单的磁盘。每个容量1M ,数据都保存在内存中。 sbull的代码,在google 搜索sbull可以找到,书上也对block device的实现做了解释,而且都是以sbull这个例子来说的,书写的很好,都可以去看一下吧。

我在我的ubuntu虚拟机试了一下。代码如下,修改了一点点,以在2.6.24.版本内核上面编译通过
----------Makefile----------------------------------
# Comment/uncomment the following line to disable/enable debugging
#DEBUG = y


# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
DEBFLAGS = -O -g -DSBULL_DEBUG # "-O" is needed to expand inlines
else
DEBFLAGS = -O2
endif

EXTRA_CFLAGS += $(DEBFLAGS)
EXTRA_CFLAGS += -I..

ifneq ($(KERNELRELEASE),)
# call from kernel build system

obj-m := sbull.o

else

KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)

default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

endif

clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

depend .depend dep:
$(CC) $(EXTRA_CFLAGS) -M *.c > .depend


ifeq (.depend,$(wildcard .depend))
include .depend
endif
----------------------sbull.c-----------------
/*
* Sample disk driver, from the beginning.
*/


/*这个文件不存在了
#include <linux/config.h>
*/
#ifndef _LINUX_CONFIG_H
#define _LINUX_CONFIG_H
/* This file is no longer in use and kept only for backward compatibility.
* autoconf.h is now included via -imacros on the commandline
*/
#include <linux/autoconf.h>

#endif

#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 = 1024; /* How big the drive is */
module_param(nsectors, int, 0);
static int ndevices = 4;
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_SIMPLE;
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 30*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;

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(request_queue_t *q)
{
struct request *req;

while ((req = elv_next_request(q)) != NULL) {
   struct sbull_dev *dev = req->rq_disk->private_data;
   if (! blk_fs_request(req)) {
    printk (KERN_NOTICE "Skip non-fs request\n");
    end_request(req, 0);
    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);
   sbull_transfer(dev, req->sector, req->current_nr_sectors,
     req->buffer, rq_data_dir(req));
   end_request(req, 1);
}
}


/*
* 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_sectors(bio),
     buffer, bio_data_dir(bio) == WRITE);
   sector += bio_cur_sectors(bio);
   __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(request_queue_t *q)
{
struct request *req;
int sectors_xferred;
struct sbull_dev *dev = q->queuedata;

while ((req = elv_next_request(q)) != NULL) {
   if (! blk_fs_request(req)) {
    printk (KERN_NOTICE "Skip non-fs request\n");
    end_request(req, 0);
    continue;
   }
   sectors_xferred = sbull_xfer_request(dev, req);
   if (! end_that_request_first(req, 1, sectors_xferred)) {
    blkdev_dequeue_request(req);
    end_that_request_last(req,0);
   }
}
}

/*
* The direct make request version.
*/
static int sbull_make_request(request_queue_t *q, struct bio *bio)
{
struct sbull_dev *dev = q->queuedata;
int status;

status = sbull_xfer_bio(dev, bio);
bio_endio(bio, status);
return 0;
}


/*
* Open and close.
*/

static int sbull_open(struct inode *inode, struct file *filp)
{
struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data;

del_timer_sync(&dev->timer);
filp->private_data = dev;
spin_lock(&dev->lock);
if (! dev->users)
   check_disk_change(inode->i_bdev);
dev->users++;
spin_unlock(&dev->lock);
return 0;
}

static int sbull_release(struct inode *inode, struct file *filp)
{
struct sbull_dev *dev = inode->i_bdev->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 inode *inode, struct file *filp,
                 unsigned int cmd, unsigned long arg)
{
long size;
struct hd_geometry geo;
struct sbull_dev *dev = filp->private_data;

switch(cmd) {
     case HDIO_GETGEO:
        /*
   * Get geometry: since we are a virtual device, we have to make
   * up something plausible. So we claim 16 sectors, four heads,
   * and calculate the corresponding number of cylinders. We set the
   * start of data at sector four.
   */
   size = dev->size*(hardsect_size/KERNEL_SECTOR_SIZE);
   geo.cylinders = (size & ~0x3f) >> 6;
   geo.heads = 4;
   geo.sectors = 16;
   geo.start = 4;
   if (copy_to_user((void __user *) arg, &geo, sizeof(geo)))
    return -EFAULT;
   return 0;
}

return -ENOTTY; /* unknown command */
}

/*
* 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
};


/*
* 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);
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.
*/
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);
    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);
--------------sbull_load.sh-------------------------
#!/bin/sh

make_minors()
{
    let part=1
    while (($part < $minors)); do
let minor=$part+$2
mknod $1$part b $major $minor
let part=$part+1
    done
}


# FIXME: This isn't handling minors (partitions) at all.
module="sbull"
device="sbull"
mode="664"
chardevice="sbullr"
minors=16

# Group: since distributions do it differently, look for wheel or use staff
if grep '^staff:' /etc/group > /dev/null; then
    group="staff"
else
    group="wheel"
fi

# invoke insmod with all arguments we got
# and use a pathname, as newer modutils don't look in . by default
/sbin/insmod -f ./$module.ko $* || exit 1

major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"`

# Remove stale nodes and replace them, then give gid and perms

rm -f /dev/${device}[a-d]* /dev/${device}

mknod /dev/${device}a b $major 0
make_minors /dev/${device}a 0
mknod /dev/${device}b b $major 16
make_minors /dev/${device}b 16
mknod /dev/${device}c b $major 32
make_minors /dev/${device}c 32
mknod /dev/${device}d b $major 48
make_minors /dev/${device}d 48
ln -sf ${device}a /dev/${device}
chgrp $group /dev/${device}[a-d]*
chmod $mode /dev/${device}[a-d]*
-----------------sbull_unload.sh-------------------------------------
#!/bin/sh
module="sbull"
device="sbull"

# invoke rmmod with all arguments we got
/sbin/rmmod $module $* || exit 1

# Remove stale nodes
rm -f /dev/${device}[a-d]* /dev/${device}

--------------------------------------------------
root@widebright-desktop:/home/widebright# ls -l /dev/sbulla
brw-rw-r-- 1 root staff 254, 0 2008-12-10 17:12 /dev/sbulla
root@widebright-desktop:/home/widebright#
root@widebright-desktop:/home/widebright# ls -l /dev/sda
brw-rw---- 1 root disk 8, 0 2008-12-10 23:19 /dev/sda
root@widebright-desktop:/home/widebright# fdisk -l /dev/sbulla

Disk /dev/sbulla: 0 MB, 524288 bytes
255 heads, 63 sectors/track, 0 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0x00000000

Disk /dev/sbulla doesn't contain a valid partition table

我测试时用fdisk想分区,结果不行,可能是磁盘容量太小了,不正常操作。其他的好像还可以正常显示。

  评论这张
 
阅读(824)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017