zoukankan      html  css  js  c++  java
  • 块设备驱动之内存模拟硬盘

    转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/25240899

    一.块设备驱动框架

    app:      open,read,write "hello.txt"
    ---------------------------------------------  文件的读写
    文件系统: vfat, ext2, ext3, yaffs2, jffs2      (把文件的读写转换为扇区的读写)
    -----------------ll_rw_block-----------------  扇区的读写(临时先不分析)
                           1. 把"读写"放入队列
                           2. 调用队列的处理函数(优化/调顺序/合并)
                块设备驱动程序     
    ---------------------------------------------
    硬件:        硬盘,flash

    与字符设备相比什么差别:

              1.块设备仅仅能以块为单位接受输入和返回输出。而字符设备则以字节为单位。大多数设备是字符设备,由于它们不须要缓冲并且不以固定块大小进行操作。

              2.块设备对于I/O请求有相应的缓冲区。因此它们能够选择以什么顺序进行响应,字符设备无需缓冲且被直接读写。对于存储设备而言调整读写的顺序作用巨大,由于在读写连续的扇区比分离的扇区更快。

              3.符设备仅仅能被顺序读写,而块设备能够随机訪问。尽管块设备可随机訪问,可是对于磁盘这类机械设备而言。顺序地组织块设备的訪问能够提高性能。

    二.驱动代码

    /* 參考:
     * driverslockxd.c
     * driverslockz2ram.c
     */
    
    #include <linux/module.h>
    #include <linux/errno.h>
    #include <linux/interrupt.h>
    #include <linux/mm.h>
    #include <linux/fs.h>
    #include <linux/kernel.h>
    #include <linux/timer.h>
    #include <linux/genhd.h>
    #include <linux/hdreg.h>
    #include <linux/ioport.h>
    #include <linux/init.h>
    #include <linux/wait.h>
    #include <linux/blkdev.h>
    #include <linux/blkpg.h>
    #include <linux/delay.h>
    #include <linux/io.h>
    
    #include <asm/system.h>
    #include <asm/uaccess.h>
    #include <asm/dma.h>
    
    static struct gendisk *ramblock_disk;
    static request_queue_t *ramblock_queue;
    
    static int major;
    
    static DEFINE_SPINLOCK(ramblock_lock);
    
    #define RAMBLOCK_SIZE (1024*1024)
    static unsigned char *ramblock_buf;
    
    /*几何属性*/
    static int ramblock_getgeo(struct block_device *bdev, struct hd_geometry *geo)
    {
    	/* 容量=heads*cylinders*sectors*512 */
    	geo->heads     = 2;                       //多少面
    	geo->cylinders = 32;                      //多少环
    	geo->sectors   = RAMBLOCK_SIZE/2/32/512;  //一环有多少扇区
    	return 0;
    }
    
    
    static struct block_device_operations ramblock_fops = {
    	.owner	= THIS_MODULE,                     //一个指向拥有该结构的模块指针。通常设为 THIS_MODULE
    	.getgeo	= ramblock_getgeo,                 //几何属性
    };
    
    static void do_ramblock_request(request_queue_t * q)
    {
    	static int r_cnt = 0;
    	static int w_cnt = 0;
    	struct request *req;
    	
    	//printk("do_ramblock_request %d
    ", ++cnt);
    
    
    	while ((req = elv_next_request(q)) != NULL)    //一旦有请求req就不为null
    	{
    		/* 传输数据三要素: 源,目的,长度 */
    		/* 源/目的: */
    		unsigned long offset = req->sector * 512;    //从哪開始(偏移值)
    
    		/* 目的/源: */
    		// req->buffer
    
    		/* 长度: */		
    		unsigned long len = req->current_nr_sectors * 512;  //须要传送的扇区数
    
    		if (rq_data_dir(req) == READ)  //假设是读
    		{
    			//printk("do_ramblock_request read %d
    ", ++r_cnt);
    			memcpy(req->buffer, ramblock_buf+offset, len);  //将内存中数据复制到buffer中
    		}
    		else                           //假设是写
    		{
    			//printk("do_ramblock_request write %d
    ", ++w_cnt);
    			memcpy(ramblock_buf+offset, req->buffer, len);   //将buffer中的数据拷到内存中
    		}		
    		
    		end_request(req, 1);
    	}
    }
    
    static int ramblock_init(void)        //入口函数
    {
    	/* 1. 分配一个gendisk结构体 */
    	ramblock_disk = alloc_disk(16); /* 次设备号个数: 分区个数+1 */
    
    	/* 2. 设置 */
    	/* 2.1 分配/设置队列: 提供读写能力 */
    	ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock);
    	ramblock_disk->queue = ramblock_queue;    //把队列放入结构体
    	
    	/* 2.2 设置其它属性: 比方容量 */
    	major = register_blkdev(0, "ramblock");  /* 向内核注冊块设备。假设是0,则分配一个新的主设备号给设备。并将设备号返回给调用者cat /proc/devices */	
    	ramblock_disk->major       = major;                 //主设备号
    	ramblock_disk->first_minor = 0;                    //次设备号
    	sprintf(ramblock_disk->disk_name, "ramblock");      //名字
    	ramblock_disk->fops        = &ramblock_fops;        //操作函数
    	set_capacity(ramblock_disk, RAMBLOCK_SIZE / 512);  //容量
    
    	/* 3. 硬件相关操作 */
    	ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL); //分配一块内存
    
    	/* 4. 注冊 */
    	add_disk(ramblock_disk);        //一点调用它,磁盘设备将被“激活”,并随时会调用它提供方法
    
    	return 0;
    }
    
    static void ramblock_exit(void)         //出口函数
    {
    	unregister_blkdev(major, "ramblock"); //注销块设备驱动程序
    	del_gendisk(ramblock_disk);           //卸载磁盘
    	put_disk(ramblock_disk);
    	blk_cleanup_queue(ramblock_queue);
    
    	kfree(ramblock_buf);
    }
    
    module_init(ramblock_init);
    module_exit(ramblock_exit);
    
    MODULE_LICENSE("GPL");


    三.驱动分析

               我们的驱动程序要实现的功能就是将内存模拟硬盘进行操作。
        1. 分配gendisk结构体
        2. 设置
            2.1 分配/设置队列,并将它放入结构体中,用来提供读写能力
            2.2 设置gendisk其它信息(主设备号、次设备号、名字、操作函数、容量)

                      操作函数:这里面主要调用了ramblock_getgeo函数用来设置硬盘的属性(尽管是RAM模拟的,为了使用 老的工具还是要假装设置一下)

        3.硬件相关的操作(就是分配一块内存。比較简单)         
        4. 注冊: add_disk

        4.请求处理函数(块设备驱动的核心)

                  当内核须要驱动程序处理读取、写入以及其它的操作时。就会调用改函数。

    static void do_ramblock_request(request_queue_t * q)
    {
    	static int r_cnt = 0;
    	static int w_cnt = 0;
    	struct request *req;
    	
    	//printk("do_ramblock_request %d
    ", ++cnt);
    
    
    	while ((req = elv_next_request(q)) != NULL)    //一旦有请求req就不为null
    	{
    		/* 传输数据三要素: 源,目的,长度 */
    		/* 源/目的: */
    		unsigned long offset = req->sector * 512;    //从哪開始(偏移值)
    
    		/* 目的/源: */
    		// req->buffer
    
    		/* 长度: */		
    		unsigned long len = req->current_nr_sectors * 512;  //须要传送的扇区数
    
    		if (rq_data_dir(req) == READ)  //假设是读
    		{
    			//printk("do_ramblock_request read %d
    ", ++r_cnt);
    			memcpy(req->buffer, ramblock_buf+offset, len);  //将内存中数据复制到buffer中
    		}
    		else                           //假设是写
    		{
    			//printk("do_ramblock_request write %d
    ", ++w_cnt);
    			memcpy(ramblock_buf+offset, req->buffer, len);   //将buffer中的数据拷到内存中
    		}		
    		
    		end_request(req, 1);
    	}
    }

    这段代码首先推断是否有请求,假设有依据传输数据三要素进行设置,然后推断读写,进行对应的操作。

     

    參考:韦东山视频第二期

               LDD3



  • 相关阅读:
    [JLOI2010] 冠军调查
    [ZJOI2009] 狼和羊的故事
    [CF1451D] Circle Game
    [CF1451E1] Bitwise Queries (Easy Version)
    [CF343D] Water Tree
    [CF1344B] Monopole Magnets
    [CF191C] Fools and Roads
    [CF1370D] Odd-Even Subsequence
    [CF1366D] Two Divisors
    [CF1359D] Yet Another Yet Another Task
  • 原文地址:https://www.cnblogs.com/mthoutai/p/7039570.html
Copyright © 2011-2022 走看看