• Linux设备驱动--块设备(四)之“自造请求”(转)


    前面, 我们已经讨论了内核所作的在队列中优化请求顺序的工作; 这个工作包括排列请求和, 或许, 甚至延迟队列来允许一个预期的请求到达. 这些技术在处理一个真正的旋转的磁盘驱动器时有助于系统的性能. 但是, 许多面向块的设备, 例如闪存阵列, 用于数字相机的存储卡的读取器、u盘等, 并且 RAM 盘真正地有随机存取的性能, 包含从高级的请求队列逻辑中获益. 其他设备, 例如软件 RAID 阵列或者被逻辑卷管理者创建的虚拟磁盘, 没有这个块层的请求队列被优化的性能特征. 对于这类设备, 它最好直接从块层接收请求, 并且根本不去烦请求队列.

    这时候我们就不用内核提供的IO调度器来优化排列和合并请求,不用内核的__make_request 帮我们处理bio,而是我们自己处理bio

    数据流程

    当我们初始化一个请求队列

    struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)  
    {  
        return blk_init_queue_node(rfn, lock, -1);  
    }  

    把请求队列和这个内核已经实现好的函数绑定起来,__make_request就是负责制造请求request 的

    blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id)  
    {  
        struct request_queue *q = blk_alloc_queue_node(GFP_KERNEL, node_id);  
        ......  
        blk_queue_make_request(q, __make_request);  
        ......  
    }  
    static int __make_request(struct request_queue *q, struct bio *bio)  

    这个bio就是最基本的读写不同扇区的请求,经过__make_request处理后,经过优化返回request

    但是,在这里已经不需要了,我们要直接处理bio,来一个处理一个。

    分配“请求队列”
    request_queue_t *blk_alloc_queue(int gfp_mask);
    对于FLASH、RAM盘等完全随机访问的非机械设备,并不需要进行复杂的I/O调度,这个时候,应该使用上述函数分配1个“请求队列”,并使用如下函数来绑定“请求队列”和“制造请求”函数。
    void blk_queue_make_request(request_queue_t * q, make_request_fn * mfn);


    void blk_queue_hardsect_size(request_queue_t *queue, unsigned short max); 
    该函数用于告知内核块设备硬件扇区的大小,所有由内核产生的请求都是这个大小的倍数并且被正确对界。但是,内核块设备层和驱动之间的通信还是以512字节扇区为单位进行。

    绑定请求队列和“制造请求”函数

    void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn)

    一个"制造请求"函数来处理bio, make_request 函数有这个原型:

    typedef int (make_request_fn) (request_queue_t *q, struct bio *bio);  

    参考代码:

    #include <linux/init.h>  
    #include <linux/module.h>  
    #include <linux/kernel.h>  
    #include <linux/fs.h>  
    #include <linux/errno.h>  
    #include <linux/types.h>  
    #include <linux/fcntl.h>  
    #include <linux/vmalloc.h>  
    #include <linux/hdreg.h>  
    #include <linux/blkdev.h>  
    #include <linux/blkpg.h>  
    #include <asm/uaccess.h>  
      
    #define BLK_NAME "ram_blk"  
    #define BLK_MAJOR 222  
    #define DISK_SECTOR_SIZE 512 //每扇区大小  
    #define DISK_SECTOR 1024  //总扇区数,  
    #define DISK_SIZE (DISK_SECTOR_SIZE*DISK_SECTOR)//总大小,共0.5M  
      
    typedef struct//设备结构体  
    {  
           unsigned char          *data;  
           struct request_queue   *queue;  
           struct gendisk         *gd;  
    } disk_dev;  
      
    disk_dev device;//定义设备结构体  
      
    //--------------------------------------------------------------------------  
    //在硬盘等带柱面扇区等的设备上使用request,可以整理队列。但是ramdisk等可以  
    //使用make_request  
    static int disk_make_request(struct request_queue *q,struct bio *bio)  
    {  
           int i;  
           char *mem_pbuf;  
           char *disk_pbuf;  
           disk_dev *pdevice;  
           struct bio_vec *pbvec;  
           /*在遍历段之前先判断要传输数据的总长度大小是否超过范围*/  
           i=bio->bi_sector*DISK_SECTOR_SIZE+bio->bi_size;  
           if(i>DISK_SIZE)//判断是否超出范围  
                  goto fail;  
             
           pdevice=(disk_dev*)bio->bi_bdev->bd_disk->private_data;//得到设备结构体  
           disk_pbuf=pdevice->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);  
                                goto fail;  
                  }  
                  kunmap(pbvec->bv_page);//清除映射  
                  disk_pbuf+=pbvec->bv_len;  
           }  
           bio_endio(bio,0);//这个函数2.6.25和2.6.4是不一样的,  
           return 0;  
    fail:  
           bio_io_error(bio);//这个函数2.6.25和2.6.4是不一样的,  
           return 0;  
    }  
      
    int blk_open(struct block_device *dev, fmode_t no)   
    {  
           return 0;  
    }  
      
    int blk_release(struct gendisk *gd, fmode_t no)  
    {  
           return 0;  
    }  
      
    int blk_ioctl(struct block_device *dev, fmode_t no, unsigned cmd, unsigned long arg)  
    {  
           return -ENOTTY;  
    }  
      
    static struct block_device_operations blk_fops=  
    {  
           .owner=THIS_MODULE,  
           .open=blk_open,//  
           .release=blk_release,//  
           .ioctl=blk_ioctl,//   
    };  
      
    int disk_init(void)  
    {  
            if(!register_blkdev(BLK_MAJOR,BLK_NAME));//注册驱动  
        {  
             printk("register blk_dev succeed
    ");  
        }  
          
           device.data=vmalloc(DISK_SIZE);  
           device.queue=blk_alloc_queue(GFP_KERNEL);//生成队列  
           blk_queue_make_request(device.queue,disk_make_request);/*注册make_request  绑定请求制造函数*/  
      
        printk("make_request succeed
    ");  
      
           device.gd=alloc_disk(1);//生成gendisk  
           device.gd->major=BLK_MAJOR;//主设备号  
           device.gd->first_minor=0;//此设备号  
           device.gd->fops=&blk_fops;//块文件结构体变量  
           device.gd->queue=device.queue;//请求队列  
           device.gd->private_data=&device;  
           sprintf(device.gd->disk_name,"disk%c",'a');//名字  
           set_capacity(device.gd,DISK_SECTOR);//设置大小  
           add_disk(device.gd);//注册块设备信息  
        printk("gendisk succeed
    ");      
           return 0;  
    }  
      
    void disk_exit(void)  
    {  
          
           del_gendisk(device.gd);  
           put_disk(device.gd);  
           unregister_blkdev(BLK_MAJOR,BLK_NAME);  
           vfree(device.data);  
            printk("free succeed
    ");  
          
    }  
      
    module_init(disk_init);  
    module_exit(disk_exit);  
      
    MODULE_LICENSE("Dual BSD/GPL");  

    http://blog.csdn.net/jianchi88/article/details/7213290

  • 相关阅读:
    BOM 事件 navigator浏览器的判断
    闭包
    超简单超实用的走马灯效果实现
    对DOM的增删改查
    SVN版本回退与常用命令总结
    mongodb使用总结
    如何理解 IE 的文档兼容模式
    软件开发编码规范
    第一篇绑定数据 CMS
    关于IIS7.5下的web.config配置的一些问题
  • 原文地址:https://www.cnblogs.com/cainiaoaixuexi/p/3713208.html
Copyright © 2020-2023  润新知