编写简单的ramdisk(有请求队列)
前言
前面用无请求队列实现的ramdisk的驱动程序虽然申请了请求队列,但实际上没用上,因为ramdisk不像实际的磁盘访问速度慢需要缓存,ramdisk之间使用内存空间,所以就没用请求队列了。本文将介绍使用请求队列的ramdisk驱动,虽然对于ramdisk使用请求队列用处不大,但对于基于磁盘的块设备驱动来说却是必须要用的。
在LDD3书中,其中的有些块设备操作函数在当前的linux版本中有了很大的变动,需要自己重新根据新定义的一些函数进行适当的移植,以解决编译时报出的各种错误,主要是在请求处理函数中修改。
设置请求队列的请求处理函数
在前面用无请求队列实现的ramdisk的驱动程序中直接用blk_alloc_queue来分配请求队列,这样分配的请求队列是没有请求处理函数的,之所以可以这样是因为当时就没有使用请求队列,来自上层的请求都被自定义的请求提交函数处理了。现在如果要使用请求队列,那么就必须要对请求队列初始化其请求处理函数,内核提供了函数blk_init_queue,该函数以自定义的请求函数地址作为参数传来被调用,它既实现了blk_alloc_queue的功能,还初始化了队列的请求处理函数和请求提交函数,其中请求提交函数设置为通用函数blk_queue_bio。这样来自上层的请求通过请求队列被分发到我们自定义的请求处理函数来处理。
//分配一个请求队列
simp_blkdev_queue = blk_init_queue(simp_blkdev_do_request, NULL);
if(!simp_blkdev_queue)
{
ret = -ENOMEM;
goto blk_init_queue;
}
上面的simp_blkdev_do_request为自定义的请求处理函数。
实现请求处理函数
请求处理函数实现类似于在无请求队列ramdisk驱动中提交请求函数,说白了它们都是对上层请求的处理,只不过前者处理的是多个bio链表(每个请求时一个bio链表),后者仅仅处理一个bio而已。我们只需要遍历每个bio链表中的每个bio,并根据当前请求的类型来做相应的处理就可以了。值得注意的是自2.6.31内核开始,一些函数发生变化(见linux/include/blkdev.h)。在2.6.32内核中,
request -> sectors 变为 blk_rq_pos(request)
request -> nr_sectors 变为 blk_rq_nr_sectors(request)
elv_next_request(request) 变为 blk_fetch_request(request)
end_request(request, error) 变为 __blk_end_request_all(req, err))
static void simp_blkdev_do_request(struct request_queue *q)
{
struct request *req;
struct req_iterator ri;
struct bio_vec *bvec;
char *disk_mem;
char *buffer; //依次从队列中获取request
while ((req = blk_fetch_request(q)) != NULL) {
//判断当前request是否合法
if ((blk_rq_pos(req) << ) + blk_rq_cur_bytes(req)
> SIMP_BLKDEV_BYTES) {
printk(KERN_ERR SIMP_BLKDEV_DISKNAME
": bad request: block=%llu, count=%u\n",
(unsigned long long)blk_rq_pos(req),
blk_rq_cur_bytes(req));
blk_end_request_all(req, -EIO);
continue;
}
//获取需要操作的内存位置
disk_mem = simp_blkdev_data + (blk_rq_pos(req) << );
switch (rq_data_dir(req)) { //判断请求的类型
case READ:
rq_for_each_segment(bvec, req, ri)
{
buffer = kmap(bvec->bv_page) + bvec->bv_offset;
memcpy(buffer, disk_mem, bvec->bv_len);
kunmap(bvec->bv_page);
disk_mem += bvec->bv_len;
} /*memcpy(req->buffer,
simp_blkdev_data + (blk_rq_pos(req) << 9),
blk_rq_cur_bytes(req));*/
__blk_end_request_all(req, );
break;
case WRITE:
rq_for_each_segment(bvec, req, ri)
{
buffer = kmap(bvec->bv_page) + bvec->bv_offset;
memcpy(disk_mem, buffer, bvec->bv_len);
kunmap(bvec->bv_page);
disk_mem += bvec->bv_len;
}
/*memcpy(simp_blkdev_data + (blk_rq_pos(req) << 9),
req->buffer, blk_rq_cur_bytes(req));*/
__blk_end_request_all(req, );
break;
default:
/* No default because rq_data_dir(req) is 1 bit */
break;
}
}
}
需要注意的是结束请求需要用__blk_end_request_all函数,不能使用blk_end_request_all,这两个函数的唯一区别就是前者不会去获取队列锁,后者会尝试获取队列锁,用后者会导致系统死锁,并是系统崩溃(反正开始我是崩溃了几次)。上面代码中的rq_for_each_segment是用来遍历一个请求中的所有segment,和bio_for_each_segment类似。
编写简单的ramdisk(有请求队列)的更多相关文章
- 编写简单的ramdisk(无请求队列)
最近在研究块设备驱动的编写,看了赵磊大牛的<写一个块设备驱动>,受益匪浅,虽然能看懂里面说的,但动手写写代码还是能加深理解的,下面实现的ramdisk写的很简单,如果有错误,欢迎大牛们指正 ...
- 编写简单的ramdisk(选择IO调度器)
前言 目前linux中包含anticipatory.cfq.deadline和noop这4个I/O调度器.2.6.18之前的linux默认使用anticipatory,而之后的默认使用cfq.我们在前 ...
- SLAM+语音机器人DIY系列:(二)ROS入门——6.编写简单的service和client
摘要 ROS机器人操作系统在机器人应用领域很流行,依托代码开源和模块间协作等特性,给机器人开发者带来了很大的方便.我们的机器人“miiboo”中的大部分程序也采用ROS进行开发,所以本文就重点对ROS ...
- Python 利用Python编写简单网络爬虫实例3
利用Python编写简单网络爬虫实例3 by:授客 QQ:1033553122 实验环境 python版本:3.3.5(2.7下报错 实验目的 获取目标网站“http://bbs.51testing. ...
- Python 利用Python编写简单网络爬虫实例2
利用Python编写简单网络爬虫实例2 by:授客 QQ:1033553122 实验环境 python版本:3.3.5(2.7下报错 实验目的 获取目标网站“http://www.51testing. ...
- 学习 Linux,101: 自定义或编写简单脚本【转】
转自:http://www.ibm.com/developerworks/cn/linux/l-lpic1-105-2/index.html 学习如何使用标准的 shell 语法.循环和控制结构,以及 ...
- Flutter之Dio引入和简单的Get/Post请求
先在pubspec.yaml中引入Dio包如图所示 认识Dio库:dio是一个dart的 http请求通用库,目前也是大陆使用最广泛的库,国人开发,完全开源. flutter的插件包管理:学了引入di ...
- golang API 请求队列
概要 实现思路 使用方法 启动队列服务 使用队列服务 概要 在调用第三方 API 的时候, 基本都有访问限速的限制条件. 第三方的 API 有多个的时候, 就不太好控制访问速度, 常常会导致 HTTP ...
- 编写简单的Mapreduce程序并部署在Hadoop2.2.0上运行
今天主要来说说怎么在Hadoop2.2.0分布式上面运行写好的 Mapreduce 程序. 可以在eclipse写好程序,export或用fatjar打包成jar文件. 先给出这个程序所依赖的Mave ...
随机推荐
- ATL封装IE内核启示:使用Win32/ATL建立窗口
开发大型GUI界面程序MFC当仁不让,但如果是开发图形应用程序,并不需要大规模界面控件,没有必要链接庞大的MFC库,直接使用platform sdk会很麻烦,这时ATL中的关于Windows的封装就是 ...
- spring里的controller之间的跳转
未测试: this.getServletContext().getRequestDispatcher("/rentHouse.htm?method=display").forwar ...
- spring和Hibernate整合
首先导入spring和Hibernate的jar包,以及JDBC和c3p0的jar包, 然后就是Hibernate的XML配置文件,不需要加入映射文件,这里用spring容器管理了. Hibernat ...
- js,css控制网页内容不让选中和复制
---恢复内容开始--- JS, CSS控制网页内容不让选择和复制 CSS 控制: <style> body{ -moz-user-select:none;//针对火狐浏览器,谷歌则-we ...
- java身份证验证
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; impor ...
- 跟服务器交互的登录Demo
服务器写死 账号密码,演示登录 服务器代码: 开发工具MyEclipse public class LoginServlet extends HttpServlet { /** * The doGet ...
- DotNet Core 介绍
前言 asp.net core rtm 6月底即将发布,自己也想着为社区做点共享,刚好最近不太忙,看到社区的小伙伴们都在为dotnet core的推广而贡献力量,项目中刚好在用rc2版本,就多写些文章 ...
- .Net开发笔记(二十)创建一个需要授权的第三方组件
在使用需要授权的软件时,注册付费的目标是软件的使用者,我们开发人员平时用到的一些第三方组件也是需要授权付费的,也就是说,付费者是开发人员,并不是系统(使用了该第三方组件)的最终使用者. 以上两者的区别 ...
- 企业IT管理员IE11升级指南【4】—— IE企业模式介绍
企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...
- 比官方教程代码更简短的SignalR Server Broadcast示例
SignalR是微软ASP.NET技术体系中的新成员. 在www.asp.net网站上的SignalR专区有一篇SignalR的入门级教程<Tutorial: Server Broadcast ...