Linux驱动----块驱动开发实例


Linux驱动----块驱动开发实例
详细原理不介绍, 参照《Linux 设备驱动 Edition 3》中第16章块设备驱动. 本文把Linux设备驱动示例代码sbull移植到2.6.32版本,对应于发行版redhat6,centos6。由于对sbull比较复杂,便于理解,进行相应裁剪,去掉对功能没有影响代码,具体原因测试过程,可以不修改(你觉得无关紧要或者无法确定代码),看看会出现什么问题,这样会理解更深。
注意:
1.本人内核版本:
# uname -r
2.6.32-71.el6

2.发行版:
# cat /etc/redhat-release
CentOS Linux release 6.0 (Final)

一.测试:
这个块驱动程序,以内存代替硬盘,也就是我们常说的ramdisk,既然这样,就可以实现文件系统任何操作。例如:磁盘分区, 文件系统建立, 文件系统挂载/卸载,文件操作等
1.编译,运行,加载内核模块,查看设备文件是否创建
# insmod bdevice.ko
# ls /dev/vbda
/dev/vbda

2.磁盘分区
#fdisk  /dev/vbda
Command (m for help): n
Command action
   e   extended
   p   primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-1, default 1): w
Using default value 1

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.

# ls /dev/vbda1

3.文件系统建立

# mkfs.ext3 /dev/vbda1
mke2fs 1.41.12 (17-May-2010)
文件系统标签=
操作系统:Linux
块大小=1024 (log=0)
分块大小=1024 (log=0)
Stride=0 blocks, Stripe width=0 blocks
2000 inodes, 8000 blocks
400 blocks (5.00%) reserved for the super user
第一个数据块=1
Maximum filesystem blocks=8388608
1 block group
8192 blocks per group, 8192 fragments per group
2000 inodes per group

正在写入inode表: 完成                            
Creating journal (1024 blocks): 完成
Writing superblocks and filesystem accounting information: 完成

This filesystem will be automatically checked every 20 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.

4文件系统挂载
# mount /dev/vbda1 /opt/
# cd /opt/
# ls
#dmesg
查看扇区数
write:offset:%ld,nbytes:%ld\n

#free
查看buffer的应用情况
             buffers    
Mem:          xxxx    


5.文件读写操作
# date >xxx
# cat xxx
2013年 08月 30日 星期五 09:58:29 CS
# rm xxx -f

6.dd 操作测试
#free
#dmesg -c
#dd if=/dev/zero of=aaa
#dmesg
查看扇区数
write:offset:%ld,nbytes:%ld\n
#free
查看cached的应用情况
                cached
Mem:             xxxx


#dmesg -c
#dd if=/dev/zero of=/dev/vbda
#dmesg
查看扇区数
write:offset:%ld,nbytes:%ld\n
#free
查看cached的应用情况
                cached
Mem:             xxxxx


7.文件系统卸载
# umount /opt/

8.内核模块卸载
# rmmod bdevice.ko

思考:
   什么情况下,消耗cached?
   什么情况下,消耗buffers?
#echo 3 >/proc/sys/vm/drop_caches

二.代码
==================================
#include <linux/init.h>
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/spinlock.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h>  /* invalidate_bdev */
#include <linux/bio.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
#define KERNEL_SECTOR_SIZE      512

static int major, index=0;
static int hardsect_size = 512;
static int nsectors = 1024*2*10;     /* How big the drive is */
#define DONE_OK 0;
struct virt_dev {
    spinlock_t lock;
      int size;                       /* Device size in sectors */
        u8 *data;                       /* The data array */
    //unsigned long ioaddr;
    //unsigned int irq;
    struct gendisk *disk;
    struct request_queue *queue;
};

static struct virt_dev *vbd;
static const struct block_device_operations vbd_fops = {
        .owner  = THIS_MODULE,
};

/*
 *  * Handle an I/O request.
 *   */
static void sbull_transfer(struct virt_dev *dev, unsigned long sector,
                unsigned long nsect, char *buffer, int write)
{
    if(write)
        printk("write:offset:%ld,nbytes:%ld\n", sector, nsect);
    else
        printk("read:offset:%ld,nbytes:%ld\n", sector, nsect);
        unsigned long offset = sector*512;
        unsigned long nbytes = nsect*512;

        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);
}

/*
 *  * Transfer a single BIO.
 *   */
static int sbull_xfer_bio(struct virt_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) {
          printk("sbull_xfer_bio\n");
                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 virt_dev *dev, struct request *req)
{
        struct bio *bio;
        int nsect = 0;

        __rq_for_each_bio(bio, req) {
        printk("sbull_xfer_request\n");
                sbull_xfer_bio(dev, bio);
                nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
        }
        return nsect;
}

static void do_vbd_request(struct request_queue *q)
{
    struct request *req;
    int sectors_xferred;
    while ((req = blk_peek_request(q)) != NULL) {
        printk("do_vbd_request begin\n");
        blk_start_request(req);
    
        if (blk_fs_request(req)) {
            struct virt_dev *dev = req->rq_disk->private_data;
            /*
            do{
                printk("sectors:%d\n",blk_rq_cur_sectors(req));
                sbull_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req),
                        req->buffer, rq_data_dir(req));

            } while(__blk_end_request_cur(req, 0));
            */
              sectors_xferred = sbull_xfer_request(dev, req);

        }
        __blk_end_request_all(req, 0);
        
    }

}

static int setup_vblk(void)
{
    int err;
    vbd = kmalloc(sizeof(struct virt_dev), GFP_KERNEL);
    if(!vbd) {
        err= -ENOMEM;
        return err;
    }
    spin_lock_init(&vbd->lock);
    vbd->size = nsectors*hardsect_size;
        vbd->data = vmalloc(vbd->size);
    memset (vbd->data, 0, vbd->size);
    vbd->disk = alloc_disk(1 << 4);
        if (!vbd->disk) {
                err = -ENOMEM;
        goto out_free;
        }
    vbd->disk->queue = blk_init_queue(do_vbd_request, &vbd->lock);
       if (!vbd->disk->queue) {
                err = -ENOMEM;
                goto out_put_disk;
        }

    sprintf(vbd->disk->disk_name, "vbd%c", 'a'+ index % 26);
    printk("vbd->disk->disk_name=%s\n" ,vbd->disk->disk_name);
    vbd->disk->major = major;
    vbd->disk->first_minor = 0;
    vbd->disk->private_data = vbd;
    vbd->disk->fops = &vbd_fops;
    set_capacity(vbd->disk, nsectors);
    add_disk(vbd->disk);
        return 0;
out_put_disk:
     put_disk(vbd->disk);
out_free:
    kfree(vbd);
    return err;
}
 
static int __init init(void)
{
    int err;
    major = register_blkdev(0, "vblk");
    if(major < 0)
        return major;
    printk("major=%d\n" , major);
    
    err = setup_vblk();
    if (err) {
        printk("setup_vblk err\n");
        goto out_err;
    }
    return 0;    
out_err:
    unregister_blkdev(major, "vblk");
    return err;
}

static void __exit fini(void)
{
    printk("%s\n", __func__);
    del_gendisk(vbd->disk);
    blk_cleanup_queue(vbd->disk->queue);
    put_disk(vbd->disk);
    kfree(vbd);
    unregister_blkdev(major,"vblk");
}
MODULE_LICENSE("GPL");
module_init(init);
module_exit(fini);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值