转载:谢谢原作者:块设备驱动实战基础篇三 (BIO请求回调机制)
1.5 块设备请求返回处理回调机制
本节我们继续完善1.4节中的代码,在上节我们完成了请求的过滤转发,那么请求被磁盘处理完成后返回回来的路径处理是怎样的,本节我们继续带着这样的问题再一次完善我们的驱动程序,通过本节的学习,我们能够真正掌握请求处理,转发过滤,请求完成后回调处理机制的完整学习。
先给出完善后的IO架构图,我们对比一下1.4节最后给出的图有何区别:
相比1.4节,在fbd_driver框图右侧增加了fbd_driver end io function处理模块,底层设备sd#的请求返回后,进入fbd_driver的请求回调处理函数中。我们再次贴一下完善后的代码。
1 #ifndef _FBD_DRIVER_H
2 #define _FBD_DRIVER_H
3 #include <linux/init.h>
4 #include <linux/module.h>
5 #include <linux/blkdev.h>
6 #include <linux/bio.h>
7 #include <linux/genhd.h>
8
9 #define SECTOR_BITS (9)
10 #define DEV_NAME_LEN 32
11
12 #define DRIVER_NAME "filter driver"
13
14 #define DEVICE1_NAME "fbd1_dev"
15 #define DEVICE1_MINOR 0
16 #define DEVICE2_NAME "fbd2_dev"
17 #define DEVICE2_MINOR 1
18
19 struct fbd_dev {
20 struct request_queue *queue;
21 struct gendisk *disk;
22 sector_t size; /* devicesize in Bytes */
23 char lower_dev_name[DEV_NAME_LEN];
24 struct block_device *lower_bdev;
25 };
26
27 struct bio_context {
28 void *old_private;
29 void *old_callback;
30 };
31 #endif
1 /**
2 * fbd-driver - filter blockdevice driver
3 * Author: Talk@studio
4 **/
5 #include "fbd_driver.h"
6
7 static int fbd_driver_major = 0;
8
9 static struct fbd_dev fbd_dev1 =
10 {
11 .queue = NULL,
12 .disk = NULL,
13 .lower_dev_name = "/dev/sdb",
14 .lower_bdev = NULL,
15 .size = 0
16 };
17
18 static struct fbd_dev fbd_dev2 =
19 {
20 .queue = NULL,
21 .disk = NULL,
22 .lower_dev_name = "/dev/sdc",
23 .lower_bdev = NULL,
24 .size = 0
25 };
26
27 static int fbddev_open(struct inode *inode,struct file *file);
28 static int fbddev_close(struct inode*inode, struct file *file);
29
30 static struct block_device_operationsdisk_fops = {
31 .open = fbddev_open,
32 .release = fbddev_close,
33 .owner = THIS_MODULE,
34 };
35
36 static int fbddev_open(struct inode *inode,struct file *file)
37 {
38 printk("device is opened by:[%s]\n", current->comm);
39 return 0;
40 }
41
42 static int fbddev_close(struct inode*inode, struct file *file)
43 {
44 printk("device is closed by:[%s]\n", current->comm);
45 return 0;
46 }
47
48 static int fbd_io_callback(struct bio *bio,unsigned int bytes_done, int error)
49 {
50 struct bio_context *ctx = bio->bi_private;
51
52 bio->bi_private = ctx->old_private;
53 bio->bi_end_io = ctx->old_callback;
54 kfree(ctx);
55
56 printk("returned [%s] io request, end on sector %llu!\n",
57 bio_data_dir(bio) == READ ?"read" : "write",
58 bio->bi_sector);
59
60 if (bio->bi_end_io) {
61 bio->bi_end_io(bio,bytes_done, error);
62 }
63
64 return 0;
65 }
66
67 static int make_request(structrequest_queue *q, struct bio *bio)
68 {
69 struct fbd_dev *dev = (struct fbd_dev *)q->queuedata;
70 struct bio_context *ctx;
71
72 printk("device [%s] recevied [%s] io request, "
73 "access on dev sector[%llu], length is [%u] sectors.\n",
74 dev->disk->disk_name,
75 bio_data_dir(bio) == READ ?"read" : "write",
76 bio->bi_sector,
77 bio_sectors(bio));
78
79 ctx = kmalloc(sizeof(struct bio_context), GFP_KERNEL);
80 if (!ctx) {
81 printk("alloc memory forbio_context failed!\n");
82 bio_endio(bio,bio->bi_size, -ENOMEM);
83 goto out;
84 }
85 memset(ctx, 0, sizeof(struct bio_context));
86
87 ctx->old_private = bio->bi_private;
88 ctx->old_callback = bio->bi_end_io;
89 bio->bi_private = ctx;
90 bio->bi_end_io = fbd_io_callback;
91
92 bio->bi_bdev = dev->lower_bdev;
93 submit_bio(bio_rw(bio), bio);
94 out:
95 return 0;
96 }
97
98 static int dev_create(struct fbd_dev *dev,char *dev_name, int major, int minor)
99 {
100 int ret = 0;
101
102 /* init fbd_dev */
103 dev->disk = alloc_disk(1);
104 if (!dev->disk) {
105 printk("alloc diskerror");
106 ret = -ENOMEM;
107 goto err_out1;
108 }
109
110 dev->queue =blk_alloc_queue(GFP_KERNEL);
111 if (!dev->queue) {
112 printk("alloc queueerror");
113 ret = -ENOMEM;
114 goto err_out2;
115 }
116
117 /* init queue */
118 blk_queue_make_request(dev->queue,make_request);
119 dev->queue->queuedata = dev;
120
121 /* init gendisk */
122 strncpy(dev->disk->disk_name, dev_name,DEV_NAME_LEN);
123 dev->disk->major = major;
124 dev->disk->first_minor = minor;
125 dev->disk->fops =&disk_fops;
126
127 dev->lower_bdev =open_bdev_excl(dev->lower_dev_name, FMODE_WRITE | FMODE_READ,dev->lower_bdev);
128 if (IS_ERR(dev->lower_bdev)) {
129 printk("Open thedevice[%s]'s lower dev [%s] failed!\n", dev_name, dev->lower_dev_name);
130 ret = -ENOENT;
131 goto err_out3;
132 }
133
134 dev->size =get_capacity(dev->lower_bdev->bd_disk) << SECTOR_BITS;
135
136 set_capacity(dev->disk,(dev->size >> SECTOR_BITS));
137
138 /* bind queue to disk */
139 dev->disk->queue = dev->queue;
140
141 /* add disk to kernel */
142 add_disk(dev->disk);
143 return 0;
144err_out3:
145 blk_cleanup_queue(dev->queue);
146err_out2:
147 put_disk(dev->disk);
148err_out1:
149 return ret;
150 }
151
152 staticvoid dev_delete(struct fbd_dev *dev, char *name)
153 {
154 printk("delete the device[%s]!\n", name);
155 close_bdev_excl(dev->lower_bdev);
156
157 blk_cleanup_queue(dev->queue);
158 del_gendisk(dev->disk);
159 put_disk(dev->disk);
160 }
161
162 staticint __init fbd_driver_init(void)
163 {
164 int ret;
165
166 /* register fbd driver, get the drivermajor number*/
167 fbd_driver_major =register_blkdev(fbd_driver_major, DRIVER_NAME);
168 if (fbd_driver_major < 0) {
169 printk("get majorfail");
170 ret = -EIO;
171 goto err_out1;
172 }
173
174 /* create the first device */
175 ret = dev_create(&fbd_dev1, DEVICE1_NAME,fbd_driver_major, DEVICE1_MINOR);
176 if (ret) {
177 printk("create device[%s] failed!\n", DEVICE1_NAME);
178 goto err_out2;
179 }
180
181 /* create the second device */
182 ret = dev_create(&fbd_dev2,DEVICE2_NAME, fbd_driver_major, DEVICE2_MINOR);
183 if (ret) {
184 printk("create device[%s] failed!\n", DEVICE2_NAME);
185 goto err_out3;
186 }
187 return ret;
188err_out3:
189 dev_delete(&fbd_dev1,DEVICE1_NAME);
190err_out2:
191 unregister_blkdev(fbd_driver_major,DRIVER_NAME);
192err_out1:
193 return ret;
194 }
195
196 staticvoid __exit fbd_driver_exit(void)
197 {
198 /* delete the two devices */
199 dev_delete(&fbd_dev2,DEVICE2_NAME);
200 dev_delete(&fbd_dev1,DEVICE1_NAME);
201
202 /* unregister fbd driver */
203 unregister_blkdev(fbd_driver_major,DRIVER_NAME);
204 printk("block device driver exitsuccessfuly!\n");
205 }
206
207module_init(fbd_driver_init);
208module_exit(fbd_driver_exit);
209MODULE_LICENSE("GPL");
先看头文件的变化,27-30行我们新定义了一个数据结构bio_context,顾名思义该数据结构用于描述bio请求的上下文信息,里面有两个成员,都是指针,old_private指针用于保存bio中private指针内容,old_callback用于保存bio中旧的回调函数指针。
接下来分析fbd_driver.c,我们直接看make_request函数,67-96行。
67 staticint make_request(struct request_queue *q, struct bio *bio)
68 {
69 struct fbd_dev *dev = (struct fbd_dev *)q->queuedata;
70 struct bio_context *ctx;
71
72 printk("device [%s] recevied [%s] io request, "
73 "access on dev sector[%llu], length is [%u] sectors.\n",
74 dev->disk->disk_name,
75 bio_data_dir(bio) == READ ?"read" : "write",
76 bio->bi_sector,
77 bio_sectors(bio));
78
79 ctx = kmalloc(sizeof(struct bio_context), GFP_KERNEL);
80 if (!ctx) {
81 printk("alloc memory forbio_context failed!\n");
82 bio_endio(bio,bio->bi_size, -ENOMEM);
83 goto out;
84 }
85 memset(ctx, 0, sizeof(struct bio_context));
86
87 ctx->old_private = bio->bi_private;
88 ctx->old_callback = bio->bi_end_io;
89 bio->bi_private = ctx;
90 bio->bi_end_io = fbd_io_callback;
91
92 bio->bi_bdev = dev->lower_bdev;
93 submit_bio(bio_rw(bio), bio);
94 out:
95 return 0;
96 }
70行定义了一个bio_context指针变量,79行通过kmalloc函数为ctx指针申请了内存并赋值,这样我们申请bio的上下文信息数据结构,并在85行用memset函数将ctx数据结构清空,接下来87-88行,我们保存了bio中的bi_private指针和bi_end_io回调函数指针,在90-91行把bio中的这两个指针重新赋值了,bi_private赋值为ctx指针值,bi_end_io回调函数指针赋值为fbd_io_callback函数指针,这样我们完成了bio请求的再次加工处理,我们趁热打铁继续分析fbd_io_callback函数一探究竟,搞清楚这么的做的原因。
48 static int fbd_io_callback(struct bio *bio,unsigned int bytes_done, int error)
49 {
50 struct bio_context *ctx = bio->bi_private;
51
52 bio->bi_private = ctx->old_private;
53 bio->bi_end_io = ctx->old_callback;
54 kfree(ctx);
55
56 printk("returned [%s] io request, end on sector %llu!\n",
57 bio_data_dir(bio) == READ ?"read" : "write",
58 bio->bi_sector);
59
60 if (bio->bi_end_io) {
61 bio->bi_end_io(bio,bytes_done, error);
62 }
63
64 return 0;
65 }
输入参数是bio指针以及请求完成的子结束和错误标志,由于在90行我们把bio中bi_private的指针赋值到ctx指针中,这样50行的代码我们就可以重新获取ctx指针,进而52-53行我们重新恢复bio的bi_private/bi_end_io指针值,最后54行释放ctx,因为此时ctx中的指针值我们已恢复,ctx已不再有用可以释放其占用的内存,我们继续调用bio原本就注册好的bi_end_io回调函数,完成真正的请求处理结束操作。
至此,我们走完了完整的请求处理,转发过滤,完成回调全过程。
P.S.: 注意此篇并没有深入剖析硬中断->软中断->bio回调处理过程,我们先知道请求会通过我们注册的回调函数返回即可,后续再深入剖析操作系统细节。
转载:谢谢原作者:块设备驱动实战基础篇三 (BIO请求回调机制)相关推荐
- 转载:谢谢原作者: 块设备驱动实战基础篇二 (继续完善170行过滤驱动代码至200行)
1.3块设备驱动关键数据结构及函数API详细剖析 经过上节四个步骤我们已经熟悉并实战了一个最基本的过滤块设备驱动的设计技巧,我们这一节先不继续实战,我们本节把上节170行代码中接触到的块设备核心数据结 ...
- 转载:谢谢原作者:块设备驱动实战基础篇一 (170行代码构建一个逻辑块设备驱动)
1 内核块设备驱动基础学习与实战 1.1 设备驱动IO架构初探 操作系统是如何将数据读到缓冲区的,发生了什么?我们带着这样的问题,粗略走一下read调用系统过程,希望这个初探,可以唤起大家研究操作 ...
- 转载:谢谢原作者:块设备驱动实战基础篇四 (逐渐成型,加入ioctl通信机制)
1.6介绍一种内核与用户空间通信的方法-misc设备ioctl机制 块设备驱动开发中往往需要配合用户态的管理程序工具,管理我们的块设备,此时我们需要涉及用户空间程序与块设备通信的方法,ioctl机制就 ...
- linux块设备驱动编写,Linux内核学习笔记 -49 工程实践-编写块设备驱动的基础
块设备可以随机存储.字符设备,比如键盘,只能按照输入顺序存取,不可随机,打乱输入的字节流. 文件系统层,包括常见的文件系统,以及虚拟文件系统层VFS,字符设备可以直接用应用程序打开.块设备不会在应用程 ...
- Linux 字符设备驱动开发基础(三)—— read()、write() 相关函数解析
我们在前面讲到了file_operations,其是一个函数指针的集合,用于存放我们定义的用于操作设备的函数的指针,如果我们不定义,它默认保留为NULL.其中有最重要的几个函数,分别是open().r ...
- 块设备驱动、bio理解
别人写过的内容,我就不写了.贴一下大佬的博客,写的非常好: 块设备驱动实战基础篇一 (170行代码构建一个逻辑块设备驱动) 块设备驱动实战基础篇二 (继续完善170行过滤驱动代码至200行) 块设备驱 ...
- linux内核的块设备驱动框架详解
1.块设备和字符设备的差异 (1)块设备只能以块为单位接受输入和返回输出,而字符设备则以字节为单位.大多数设备是字符设备,因为它们不需要缓冲而且不以固定块大小进行操作; (2)块设备对于 I/O 请求 ...
- Nginx实战基础篇六 通过源码包编译安装部署LNMP搭建Discuz论坛
Nginx实战基础篇六 通过源码包编译安装部署LNMP搭建Discuz论坛 版权声明: 本文遵循"署名非商业性使用相同方式共享 2.5 中国大陆"协议 您可以自由复制.发行.展览. ...
- Nginx实战基础篇一 源码包编译安装部署web服务器
Nginx实战基础篇一 源码包编译安装部署web服务器 版权声明: 本文遵循"署名非商业性使用相同方式共享 2.5 中国大陆"协议 您可以自由复制.发行.展览.表演.放映.广播或通 ...
最新文章
- android 打包hbuilder 高德地图加载不出来_十一黄金周地图很忙:百度获央视报道,高德忙道歉,究竟谁好用?...
- 申请成功,庆祝一下。
- 网络编程4之UDP协议
- redis数据类型hash总结
- sprinboot整合activity
- java response cookie_JavaWeb部分 (Response Cookie)
- Ubuntu环境下,反编译工具Apktool,Dex2jar,jd-gul,luyten安装使用
- Windows系统设置共享文件夹及访问共享文件夹
- 用了服务器后网站统计代码被劫持,网站劫持代码,网站被劫持怎么办 | 帮助信息-动天数据...
- R语言绘图—“金字塔图”
- ​零代码增删改查完全自动化的万能通用接口
- 上云 企业 过程_企业上云的步骤
- Docker | Docker 快速搭建 TensorRT 环境(超详细)
- shell从入门到精通(25)你知道什么是login shell和non login shell吗
- 2018世界杯所有场次记录
- CRM客户管理系统哪个好用?盘点前十名!
- TortoiseGit配合msysGit
- Python黑技术:远程控制男朋友电脑
- 他山万年历 v1.0 for Linux
- Python跨文件全局变量的使用技巧
热门文章
- 重庆工商大学c语言实验一答案,重庆工商大学嵌入式实验考试题目答案 - 副本.doc...
- java枚举.toString_java – Make Enum.toString()本地化
- 清华大学计算机毕业论文,清华大学毕业论文撰写要求
- Python应用实战案例-Python实现K线图绘制
- Python编程基础:第二十九节 异常Exception
- 多线程下HashMap的死循环
- Golang中的自动伸缩和自防御设计
- 一文揭秘定时任务调度框架quartz
- 并发工具类(三)控制并发线程数的Semaphore
- 通用前端监控采集脚本