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\n");
- }
- 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\n");
- 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\n");
- 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\n");
- }
- module_init(disk_init);
- module_exit(disk_exit);
- MODULE_LICENSE("Dual BSD/GPL");
Linux设备驱动--块设备(四)之“自造请求”的更多相关文章
- 【转】Linux设备驱动--块设备(一)之概念和框架
原文地址:Linux设备驱动--块设备(一)之概念和框架 基本概念 块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时 ...
- Linux设备驱动--块设备(四)之“自造请求”(转)
前面, 我们已经讨论了内核所作的在队列中优化请求顺序的工作; 这个工作包括排列请求和, 或许, 甚至延迟队列来允许一个预期的请求到达. 这些技术在处理一个真正的旋转的磁盘驱动器时有助于系统的性能. 但 ...
- Linux设备驱动--块设备(三)之程序设计
块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigned i ...
- Linux设备驱动--块设备(二)之相关结构体
上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...
- Linux设备驱动--块设备(三)之程序设计(转)
http://blog.csdn.net/jianchi88/article/details/7212701 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数 ...
- Linux设备驱动--块设备(二)之相关结构体(转)
上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...
- Linux设备驱动--块设备(一)之概念和框架(转)
基本概念 块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性 ...
- 3.5Linux设备驱动--块设备(一)之概念和框架☆☆
基本概念 块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性 ...
- Linux字符设备与块设备的区别与比较
Linux中I/O设备分为两类:块设备和字符设备.两种设备本身没有严格限制,但是,基于不同的功能进行了分类. (1) 字符设备:提供连续的数据流,应用程序可以顺序读取,通常不支持随机存取.相反,此类设 ...
随机推荐
- [第一波模拟\day1\T2]{分班}(divide.cpp)
[题目描述] 小N,小A,小T又大了一岁了. 现在,他们已经是高二年级的学生了.众所周知,高二的小朋友是要进行文理科分班考试的,这样子的话,三个好朋友说不定就会不分在一个班. 于是三个人决定,都考平均 ...
- URI跟URL的区别
关于URL和URI的区别,个人见解. 初学java,最近被一个概念搞得头晕脑胀,就是url和uri的概念和区别,网上查了一大通,发现各种回答眼花缭乱,有百科直接粘贴的,有胡说八道的,有故意绕来绕 ...
- Python+selenium鼠标、键盘事件
鼠标操作 现在的Web产品提供了丰富的鼠标交互方式,例如鼠标右击.双击.悬停.甚至是鼠标拖动等功能,在Webdriver中,将这些关于鼠标操作的方法封装在ActionChains类提供. 1.鼠标右击 ...
- C 的指针和内存泄漏
引言 对于任何使用 C 语言的人,如果问他们 C 语言的最大烦恼是什么,其中许多人可能会回答说是指针和内存泄漏.这些的确是消耗了开发人员大多数调试时间的事项.指针和内存泄漏对某些开发人员来说似乎令人畏 ...
- 转载:shell脚本之前的基础知识
转载地址:http://www.92csz.com/study/linux/12.htm 第十二章 学习 shell脚本之前的基础知识 日常的linux系统管理工作中必不可少的就是shell脚本,如果 ...
- JS变量写到HTML页面中并修改变量值(前台处理数据序号问题)
有时候我们在前台需要对序号进行处理,我们需要将JS变量写到页面中进行显示. 第一种方式:后台处理 第二种方式:JS中定义全局变量,然后进行显示,可以书写一个JS函数对不同的需要进行不同的替换,也就可以 ...
- linux 安装报错:pkg-config not found
linux 安装报错:pkg-config not found 使用编译安装时,在执行./configure时报如下错误: ... ... checking for pkg-config... no ...
- 【搜索引擎】SOLR VS Elasticsearch(2019技术选型参考)
SOLR是什么 (官方的解释) Solr是基于Apache Lucene构建的流行的.快速的.开源的企业搜索平台. Solr也是高度可靠.可伸缩和容错的,提供分布式索引.复制和负载平衡查询.自动故障转 ...
- Match the string--hdu1797(模拟)
http://acm.hdu.edu.cn/showproblem.php?pid=1797 就是模拟 我的思路是标记aba 和h的位置 然后就判断是否正确 就行了 还有就是 最后 fkfkfkf ...
- Spring4 基本使用
前言 虽然现在基本上是 springboot 的天下了,但是传统的 spring4 在广大的软件企业中仍然占据很大比例.一上手能用,但是要让我从无到有搭一个spring4的开发环境出来,可能会磕磕碰碰 ...