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

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

数据流程

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

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

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

  1. blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id)
  2. {
  3. struct request_queue *q = blk_alloc_queue_node(GFP_KERNEL, node_id);
  4. ......
  5. blk_queue_make_request(q, __make_request);
  6. ......
  7. }
  1. 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 函数有这个原型:

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

参考代码:

  1. #include <linux/init.h>
  2. #include <linux/module.h>
  3. #include <linux/kernel.h>
  4. #include <linux/fs.h>
  5. #include <linux/errno.h>
  6. #include <linux/types.h>
  7. #include <linux/fcntl.h>
  8. #include <linux/vmalloc.h>
  9. #include <linux/hdreg.h>
  10. #include <linux/blkdev.h>
  11. #include <linux/blkpg.h>
  12. #include <asm/uaccess.h>
  13. #define BLK_NAME "ram_blk"
  14. #define BLK_MAJOR 222
  15. #define DISK_SECTOR_SIZE 512 //每扇区大小
  16. #define DISK_SECTOR 1024  //总扇区数,
  17. #define DISK_SIZE (DISK_SECTOR_SIZE*DISK_SECTOR)//总大小,共0.5M
  18. typedef struct//设备结构体
  19. {
  20. unsigned char          *data;
  21. struct request_queue   *queue;
  22. struct gendisk         *gd;
  23. } disk_dev;
  24. disk_dev device;//定义设备结构体
  25. //--------------------------------------------------------------------------
  26. //在硬盘等带柱面扇区等的设备上使用request,可以整理队列。但是ramdisk等可以
  27. //使用make_request
  28. static int disk_make_request(struct request_queue *q,struct bio *bio)
  29. {
  30. int i;
  31. char *mem_pbuf;
  32. char *disk_pbuf;
  33. disk_dev *pdevice;
  34. struct bio_vec *pbvec;
  35. /*在遍历段之前先判断要传输数据的总长度大小是否超过范围*/
  36. i=bio->bi_sector*DISK_SECTOR_SIZE+bio->bi_size;
  37. if(i>DISK_SIZE)//判断是否超出范围
  38. goto fail;
  39. pdevice=(disk_dev*)bio->bi_bdev->bd_disk->private_data;//得到设备结构体
  40. disk_pbuf=pdevice->data+bio->bi_sector*DISK_SECTOR_SIZE;//得到要读写的起始位置
  41. /*开始遍历这个bio中的每个bio_vec*/
  42. bio_for_each_segment(pbvec,bio,i)//循环分散的内存segment
  43. {
  44. mem_pbuf=kmap(pbvec->bv_page)+pbvec->bv_offset;//获得实际内存地址
  45. switch(bio_data_dir(bio))
  46. {//读写
  47. case READA:
  48. case READ:
  49. memcpy(mem_pbuf,disk_pbuf,pbvec->bv_len);
  50. break;
  51. case WRITE:
  52. memcpy(disk_pbuf,mem_pbuf,pbvec->bv_len);
  53. break;
  54. default:
  55. kunmap(pbvec->bv_page);
  56. goto fail;
  57. }
  58. kunmap(pbvec->bv_page);//清除映射
  59. disk_pbuf+=pbvec->bv_len;
  60. }
  61. bio_endio(bio,0);//这个函数2.6.25和2.6.4是不一样的,
  62. return 0;
  63. fail:
  64. bio_io_error(bio);//这个函数2.6.25和2.6.4是不一样的,
  65. return 0;
  66. }
  67. int blk_open(struct block_device *dev, fmode_t no)
  68. {
  69. return 0;
  70. }
  71. int blk_release(struct gendisk *gd, fmode_t no)
  72. {
  73. return 0;
  74. }
  75. int blk_ioctl(struct block_device *dev, fmode_t no, unsigned cmd, unsigned long arg)
  76. {
  77. return -ENOTTY;
  78. }
  79. static struct block_device_operations blk_fops=
  80. {
  81. .owner=THIS_MODULE,
  82. .open=blk_open,//
  83. .release=blk_release,//
  84. .ioctl=blk_ioctl,//
  85. };
  86. int disk_init(void)
  87. {
  88. if(!register_blkdev(BLK_MAJOR,BLK_NAME));//注册驱动
  89. {
  90. printk("register blk_dev succeed\n");
  91. }
  92. device.data=vmalloc(DISK_SIZE);
  93. device.queue=blk_alloc_queue(GFP_KERNEL);//生成队列
  94. blk_queue_make_request(device.queue,disk_make_request);/*注册make_request  绑定请求制造函数*/
  95. printk("make_request succeed\n");
  96. device.gd=alloc_disk(1);//生成gendisk
  97. device.gd->major=BLK_MAJOR;//主设备号
  98. device.gd->first_minor=0;//此设备号
  99. device.gd->fops=&blk_fops;//块文件结构体变量
  100. device.gd->queue=device.queue;//请求队列
  101. device.gd->private_data=&device;
  102. sprintf(device.gd->disk_name,"disk%c",'a');//名字
  103. set_capacity(device.gd,DISK_SECTOR);//设置大小
  104. add_disk(device.gd);//注册块设备信息
  105. printk("gendisk succeed\n");
  106. return 0;
  107. }
  108. void disk_exit(void)
  109. {
  110. del_gendisk(device.gd);
  111. put_disk(device.gd);
  112. unregister_blkdev(BLK_MAJOR,BLK_NAME);
  113. vfree(device.data);
  114. printk("free succeed\n");
  115. }
  116. module_init(disk_init);
  117. module_exit(disk_exit);
  118. MODULE_LICENSE("Dual BSD/GPL");
 
 
 

Linux设备驱动--块设备(四)之“自造请求”的更多相关文章

  1. 【转】Linux设备驱动--块设备(一)之概念和框架

    原文地址:Linux设备驱动--块设备(一)之概念和框架 基本概念   块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时 ...

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

    前面, 我们已经讨论了内核所作的在队列中优化请求顺序的工作; 这个工作包括排列请求和, 或许, 甚至延迟队列来允许一个预期的请求到达. 这些技术在处理一个真正的旋转的磁盘驱动器时有助于系统的性能. 但 ...

  3. Linux设备驱动--块设备(三)之程序设计

    块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigned i ...

  4. Linux设备驱动--块设备(二)之相关结构体

    上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...

  5. Linux设备驱动--块设备(三)之程序设计(转)

    http://blog.csdn.net/jianchi88/article/details/7212701 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数 ...

  6. Linux设备驱动--块设备(二)之相关结构体(转)

    上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...

  7. Linux设备驱动--块设备(一)之概念和框架(转)

    基本概念   块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性 ...

  8. 3.5Linux设备驱动--块设备(一)之概念和框架☆☆

    基本概念   块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性 ...

  9. Linux字符设备与块设备的区别与比较

    Linux中I/O设备分为两类:块设备和字符设备.两种设备本身没有严格限制,但是,基于不同的功能进行了分类. (1) 字符设备:提供连续的数据流,应用程序可以顺序读取,通常不支持随机存取.相反,此类设 ...

随机推荐

  1. 【java】基础语法

    集合   单线程 并发 Lists ArrayList——基于泛型数组 LinkedList——不推荐使用 Vector——已废弃(deprecated) CopyOnWriteArrayList—— ...

  2. oracle如何向空表中添加一个类型为clob的非空列

    一般的添加非空列的步骤是:先add可以为空的列,然后update该列为一个值(比如0),最后modify该列的类型 但是遇到类型为clob的就不行了.在modify这步时报错:ORA-22296:in ...

  3. poj3207:Ikki's Story IV-Panda's Trick【2-sat tarjan】

    题目大意:圆盘上顺次安放0, 1, 2, …, n – 1的点,每次给出两个点需要连边,可以选择在圆盘的正面连边或在圆盘的反面连边,问是否存在一种方案使得所有连线不相交? 思路:本问题可以等价成:圆盘 ...

  4. bzoj3572[Hnoi2014] 世界树 虚树+dp+倍增

    [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1921  Solved: 1019[Submit][Status][Dis ...

  5. bzoj3875 【Ahoi2014】骑士游戏 spfa处理后效性动规

    骑士游戏 [故事背景] 长期的宅男生活中,JYY又挖掘出了一款RPG游戏.在这个游戏中JYY会 扮演一个英勇的骑士,用他手中的长剑去杀死入侵村庄的怪兽. [问题描述] 在这个游戏中,JYY一共有两种攻 ...

  6. Linux怎么读? Linux读音考古一日游

    Linux怎么读?  Linux读音考古一日游/*凡是准备踏入Linux大门的叉子们(N年不关注了,不知道这个称呼是否还有),都必须经历疑问 那就是linux到底怎么读? 也许有些人很容易 什么里纽克 ...

  7. Linux(4):文件属性

    文件属性: # 重点: 磁盘空间不足 和 软链接与硬链接的区别 查看文件的属性: # ls lhi 文件 [root@NEO ~]# ls -lhi /etc/hosts 130078 -rw-r-- ...

  8. react.js 组件之间的数据传递props

    /* *属性 * 1.如何传递属性 * 2.属性和状态区别和联系 * * 3.子组件都有一个props属性对象 * * 4.单线数据流(只能从父组件流向子组件,就是在父组件定义一个属性,子组件可以通过 ...

  9. Swift 了解

    本篇仅于个人小记,记录个人不熟悉的知识点儿.如若要了解更全,请前往如下网址:http://www.runoob.com/swift/swift-arrays.html 1.Swift 标记 分号:Sw ...

  10. POJ 1797 【一种叫做最大生成树的很有趣的贪心】【也可以用dij的变形思想~】

    题意: 给一个无向图,找1到n所有的路中每条路最小权值的最大值! 屌丝一开始的思想是利用dij的变形~ 但是==屌丝忘记了更新dis数组~结果TLE无数次... 说正经的~dij的变形思想是这样的if ...