前面, 我们已经讨论了内核所作的在队列中优化请求顺序的工作; 这个工作包括排列请求和, 或许, 甚至延迟队列来允许一个预期的请求到达. 这些技术在处理一个真正的旋转的磁盘驱动器时有助于系统的性能. 但是, 许多面向块的设备, 例如闪存阵列, 用于数字相机的存储卡的读取器、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, -);
}

把请求队列和这个内核已经实现好的函数绑定起来,__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,);//这个函数2.6.25和2.6.4是不一样的,
return ;
fail:
bio_io_error(bio);//这个函数2.6.25和2.6.4是不一样的,
return ;
} int blk_open(struct block_device *dev, fmode_t no)
{
return ;
} int blk_release(struct gendisk *gd, fmode_t no)
{
return ;
} 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();//生成gendisk
device.gd->major=BLK_MAJOR;//主设备号
device.gd->first_minor=;//此设备号
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 ;
} 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");

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

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. 打造终端下mutt收发邮件环境(fbterm,fetchmail,msmtp,procmail,mutt)

    实现mutt下收发邮件须要安装,mutt,fbterm,fetchmail,msmtp,procmail 下面是各配置文件.在home文件夹下,隐私信息有马赛克... .muttrc : 当中Mail ...

  2. Android requires compiler compliance level 5.0 or 6.0. Found &#39;1.4&#39; instead的解决的方法

    今天在eclipse里报这个错误:   Android requires compiler compliance level 5.0 or 6.0. Found '1.4' instead. Plea ...

  3. 控制台中使用SetTimer的提醒

    SetTimer是设置定时器,每隔一段时间执行一个操作,原型如下 UINT_PTR SetTimer( HWND hWnd, // 窗口句柄 UINT_PTR nIDEvent, // 定时器ID,多 ...

  4. C语言 - 头文件使用案例

    源代码分门别类管理,通过头文件. 放置一些函数声明,变量声明,常量定义,宏定义. hotel.h #ifndef HOTEL_H_INCLUDED #define HOTEL_H_INCLUDED # ...

  5. nyoj--523--亡命逃窜(BFS水题)

    亡命逃窜 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描述 从前有个叫hck的骑士,为了救我们美丽的公主,潜入魔王的老巢,够英雄吧.不过英雄不是这么好当的.这个可怜的娃被魔 ...

  6. Git 工具 - 子模块

    子模块 有种情况我们经常会遇到:某个工作中的项目需要包含并使用另一个项目. 也许是第三方库,或者你独立开发的,用于多个父项目的库. 现在问题来了:你想要把它们当做两个独立的项目,同时又想在一个项目中使 ...

  7. php 判断过去离现在几年的函数

    function gettime($worktime){ $time=time(); $amount=date("Y",$time)-date("Y",strt ...

  8. 给统计人讲Python(1)_科学计算库-Numpy

    本地代码是.ipynb格式的转换到博客上很麻烦,这里展示部分代码,了解更多可以查看我的git-hub:https://github.com/Yangami/Python-for-Statisticia ...

  9. springsecurity+jwt实践和学习

    1.参考资料: https://blog.csdn.net/qq924862077/article/details/83038031 https://blog.csdn.net/sxdtzhaoxin ...

  10. NVL和NVL2有什么区别,NULLIF 的使用.

    NULL指的是空值,或者非法值. NVL (expr1, expr2):expr1为NULL,返回expr2:不为NULL,返回expr1.注意两者的类型要一致 NVL2 (expr1, expr2, ...