【Linux驱动】块设备驱动(二)—— 块设备读写(使用请求队列)

块设备的操作函数并没有类似于字符驱动中的read 和write函数,要实现读写操作,只能在请求处理函数中实现。这就分为两种,是否要使用请求队列,请求队列的主要作用是管理和调度IO请求。在以下情况中,一般需要用到请求队队列:

  • 多任务环境:多个任务同时对存储设备进行读写,请求队列可以对IO请求进行排序和调度
  • 磁盘优化:磁盘是一种机械设备,其IO操作需要进行磁盘寻道等操作,非常耗时,请求队列可以将多个IO请求进行合并,可以减少磁盘的寻道次数,提高磁盘的吞吐量。

因此,本篇的重点便是使用请求队列来实现块设备的读写操作。


目录

一、请求处理函数的触发条件

二、从请求队列获取请求

三、开始进行读写操作

1、请求的结构

2、相关API

四、关闭请求

五、完整代码


一、请求处理函数的触发条件

若要对块设备进行读写操作,需要触发请求处理函数,随后块设备驱动才会调度并处理请求。那么在什么情况下会触发请求处理函数?以下是读写操作的常见触发条件。

读操作

一般涉及到访问文件内容、文件属性、文件权限的时候,会触发请求处理函数中的读操作。

① 读取数据

当应用程序执行文件读取操作(如 read 函数)时,它会向文件系统发送读取请求,并触发请求处理函数中的读操作。每一个请求内包含源数据(块设备扇区地址、读取字节数)以及目的地址(内存页地址、页偏移)。文件系统从块设备读取相应的文件块数据,并将其返回给应用程序。

② 目录遍历

当应用程序执行目录遍历操作(如 opendir、readdir 函数等)时,文件系统会在磁盘上读取目录的元数据,并返回给应用程序。

③打开文件

当应用程序打开一个文件时,文件系统需要读取磁盘上的文件元数据信息,如文件大小、访问权限等,以供应用程序使用。

写操作

一般涉及到文件内容的更新、文件创建等操作时,会触发请求处理函数中的写操作。

① 文件内容写入(追加、更新)

文件内容的写入(write)和追加(append)会向文件系统发送写入请求,并触发请求触发函数中的写操作。每一个请求内包含源数据(内存页地址、页偏移)以及目的地址(块设备扇区地址),文件系统会将应用程序提供的数据写入到指定扇区。

此外,除了文件内容的更新,文件属性(如访问权限)的更新也会触发请求处理函数。

② 文件创建

当应用程序创建一个新文件时,它会向文件系统发送创建文件的请求,并触发请求处理函数中的写操作。文件系统在磁盘上为新文件分配空间,并更新文件的元数据信息。

二、从请求队列获取请求

接下来便是正式开始实现请求处理函数,函数的形参为请求队列,对此,下面的第一步便是从请求队列中获取请求。涉及的API 在 <linux/blkdev.h>

void request_handle(struct request_queue *q);

① 方式一:获取 + 开启请求

正常来说,获取请求包含两步,第一步是获取请求,使用的API为 blk_peek_request

/*** @param q 请求队列* @return 成功返回获取到的请求结构体地址,失败返回 NULL*/
struct request *blk_peek_request(struct request_queue *q);

获取到请求以后,还没结束,需要开启请求,目的是将通知硬件设备开始处理当前请求,同时将当前请求标记为启动状态,以确保请求在处理的过程中不会被打断。其他还有数据预处理、设备准备等原因。

/*** @param rq 获取到的请求*/
void blk_start_request(struct request *rq);

② 方式二:一步到位

Linux内核提供了获取 + 开启一步到位的方式,使用的API为 blk_fetch_request,该函数其实就是对上述两个函数的使用做了一层封装。

/*** @param q 请求队列* @return 成功返回获取到的请求结构体地址,失败返回 NULL*/
struct request *blk_fetch_request(struct request_queue *q);
/*
struct request *blk_fetch_request(struct request_queue *q)
{struct request *rq;rq = blk_peek_request(q);if (rq)blk_start_request(rq);return rq;
}
*/

三、开始进行读写操作

在开始读写操作之前,需要先了解每一个请求的结构,因为既然要进行读写操作,我们需要知道源数据地址、目的地址、操作的字节数等信息。

1、请求的结构

每个request 中保存了多个 bio,bio保存着最终要读写的数据、地址等信息。bio 中的 bvec_iter 保存了块设备扇区起始地址、扇区大小等信息,bio 中的 bio_vec 保存了 RAM 页地址、剩余页长度以及页偏移等信息。

下面是 biobio_iter bio_vec 三者之间的关系

2、相关API

获得扇区起始地址

/*** @param rq 指向请求结构体的指针* @return   成功返回获取到的扇区起始地址*/
sector_t blk_rq_pos(const struct request *rq);

 获取请求所涉及的数据长度

/*** @param rq 指向请求结构体的指针* @return   成功返回数据长度,单位:字节*/
unsigned int blk_rq_bytes(const struct request *rq);

获取指定数据页的线性地址,即 RAM 中的起始页地址

/*** @param bio 指向bio结构体的指针* @return    成功返回指向指定数据页的地址*/
void *bio_data(struct bio *bio);

获取当前请求的数据传输方向(读操作、写操作)

/*** @param rq 指向请求结构体的指针* @return   返回数据传输方向。可以是 READ / WRITE*/
int rq_data_dir(struct request *rq);

四、关闭请求

与前面开启请求相对应,开启请求后将当前请求标记为开启状态,以确保在处理请求的过程中不会被打断;关闭请求意味着该请求的数据传输和相关操作已经完成,主要目的有两点:

  • 通知请求完成:通知相关的应用程序或调用者操作已经结束,允许继续执行后续的操作
  • 资源释放:在请求处理完成后,需要释放请求所占用的内存和其他资源,以便系统能够重新利用这些资源。

这里可以使用 __blk_end_request_cur 或者 blk_end_request_cur,两个函数的使用方法完全一样,他们之间的区别在于

① __blk_end_request_cur

__blk_end_request_cur是一个异步函数,它会立即结束当前的 I/O 请求,并触发相应的回调函数(例如,end_io)来进行后续处理。这意味着调用 __blk_end_request_cur 并不会等待请求的处理完成,而是立即返回,将请求的处理交给后续的回调函数来处理。

② blk_end_request_cur

blk_end_request_cur 与上面相反,是一个同步函数,它会等待当前的 I/O 请求的处理完成,并在处理完成后返回。它会等待直到请求的 end_io 回调函数执行完毕,或者出现超时或错误等情况。因此,在调用 blk_end_request_cur 的过程中,它会阻塞当前的执行线程,直到请求处理完成。

/*** @param rq     指向请求结构体的指针* @param error  代表请求处理结果,*               - 若请求处理成功,应该将 error 设置为 0*               - 若请求处理失败,则可以设置为其他非零值。*/
bool __blk_end_request_cur(struct request *rq, int error);

五、完整代码

将驱动代码重新编译并加入到内核,输入 fdisk -l,我们会发现请求处理函数被触发了,因为 fdisk -l 用于列出当前系统中所有的磁盘分区信息,在扫描设备的过程中,会涉及块设备的访问和信息读取,这就导致了请求处理函数被触发。

void request_handle(struct request_queue *q)
{printk("请求处理函数被触发!\n");struct request* req;                // 处理请求int errors = 0;                     // 请求处理状态sector_t start = 0;                 // 操作扇区的起始地址int len = 0;while ((req = blk_fetch_request(q)) != NULL){// 获得扇区起始地址start = blk_rq_pos(req);// 当前请求所涉及的字节数len = blk_rq_cur_bytes(req);// 获得bio结构体中的缓冲区指针void* buffer = bio_data(req->bio);// 判断是读操作还是写操作if (rq_data_dir(req) == READ){memcpy((uint8_t*)buffer, blkdev.diskbuf, len);printk("从块设备读数据\n");}else if(rq_data_dir(req) == WRITE){memcpy(blkdev.diskbuf, (uint8_t*)buffer, len);printk("向块设备写数据\n");}// 结束当前请求__blk_end_request_cur(req, errors);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/669167.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

跑路页面HTML源码

简单的HTMLJSCSS&#xff0c;记事本修改内容&#xff0c;喜欢的朋友可以下载 https://download.csdn.net/download/huayula/88811984

vivado RTL综合中的多线程

RTL综合中的多线程 在多处理器系统上&#xff0c;RTL合成默认情况下利用多个CPU核心&#xff08;最多四个&#xff09;来加快编译时间。同时运行的线程的最大数量会有所不同&#xff0c;具体取决于处理器的数量可在系统、操作系统和流程阶段使用&#xff08;请参阅Vivado Desi…

HTTP1.1、HTTP2、HTTP3

HTTP1.1 HTTP/1.1 相比 HTTP/1.0 性能上的改进&#xff1a; 使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。支持管道&#xff08;pipeline&#xff09;网络传输&#xff0c;只要第一个请求发出去了&#xff0c;不必等其回来&#xff0c;就可以发第二个请求出去&…

在VM虚拟机上搭建MariaDB数据库服务器

例题&#xff1a;搭建MariaDB数据库服务器&#xff0c;并实现主主复制。 1.在二台服务器中分别MariaDB安装。 2.在二台服务器中分别配置my.cnf文件&#xff0c;开启log_bin。 3.在二台服务器中分别创建专用于数据库同步的用户replication_user&#xff0c;并授权SLAVE。&#x…

Matplotlib绘制炫酷柱状图的艺术与技巧【第60篇—python:Matplotlib绘制柱状图】

文章目录 Matplotlib绘制炫酷柱状图的艺术与技巧1. 簇状柱状图2. 堆积柱状图3. 横向柱状图4. 百分比柱状图5. 3D柱状图6. 堆积横向柱状图7. 多系列百分比柱状图8. 3D堆积柱状图9. 带有误差线的柱状图10. 分组百分比柱状图11. 水平堆积柱状图12. 多面板柱状图13. 自定义颜色和样…

c#string方法对比

字符串的截取匹配操作在开发中非常常见&#xff0c;比如下面这个示例&#xff1a;我要匹配查找出来字符串数组中以“abc”开头的字符串并打印&#xff0c;我下面分别用了两种方式实现&#xff0c;代码如下&#xff1a; using System; namespace ConsoleApp23{ class Progra…

aidl复杂流程封装

1 aidl相关困扰点 1 制作步骤复杂&#xff0c;先定义然后编译&#xff0c;然后复制&#xff0c;两边都要一一对应 2 增加回调&#xff0c;自定义对象流程更加麻烦&#xff0c;还要处理对象数据流是 in 还是out。 3 一方异常怎么办&#xff0c;虽然服务端可以用 RemoteCallbackL…

Retrofit源码分析及理解

参考文档&#xff1a; 12W字&#xff1b;2022最新Android11位大厂面试专题&#xff08;一&#xff09; - 掘金 Retrofit 版本号&#xff1a;2.9.0 Retrofit简单来说&#xff0c;就是对OkHttp上层进行了封装&#xff0c;已达到用户方便使用管理网络请求的目的。 Retrofit内部有…

力扣热门100题刷题笔记 - 10. 正则表达式匹配

力扣热门100题 - 10. 正则表达式匹配 题目链接&#xff1a;10. 正则表达式匹配 题目描述&#xff1a; 给你一个字符串 s 和一个字符规律 p&#xff0c;请你来实现一个支持 . 和 * 的正则表达式匹配。 . 匹配任意单个字符 * 匹配零个或多个前面的那一个元素 所谓匹配&#xff…

asqlcell,一个超强的 Python 库!

前言 大家好&#xff0c;今天为大家分享一个超强的 Python 库 - asqlcell。 Github地址&#xff1a;https://github.com/datarho/asqlcell Python asqlcell 是一个用于执行异步数据库操作的开源库&#xff0c;它允许开发者通过异步的方式与数据库进行交互&#xff0c;提高了数…

如何使用VS Code编写小游戏并实现公网游玩本地游戏【内网穿透】

文章目录 前言1. 编写MENJA小游戏2. 安装cpolar内网穿透3. 配置MENJA小游戏公网访问地址4. 实现公网访问MENJA小游戏5. 固定MENJA小游戏公网地址 前言 本篇教程&#xff0c;我们将通过VS Code实现远程开发MENJA小游戏&#xff0c;并通过cpolar内网穿透发布到公网&#xff0c;分…

七月论文审稿GPT第2.5版:微调GPT3.5 turbo 16K和llama2 13B以扩大对GPT4的优势

前言 自去年7月份我带队成立大模型项目团队以来&#xff0c;我司至今已有5个项目组&#xff0c;其中 第一个项目组的AIGC模特生成系统已经上线在七月官网第二项目组的论文审稿GPT则将在今年3 4月份对外上线发布第三项目组的RAG知识库问答第1版则在春节之前已就绪至于第四、第…

ai平滑工具的使用方法

ai中想要使用平滑工具来处理线条&#xff0c;该怎么是哦用哪个恩&#xff1f;下面我们就来看看详细的教程。 1、我们通过一个例子演示平滑工具的使用&#xff0c;先新建文件&#xff0c;在左侧工具箱中找到平滑工具。 文章源自四五设计网-https://www.45te.com/39726.html 2、…

CSS:三列布局

三列布局是指左右两列定宽&#xff0c;中间自适应。最终效果如下&#xff1a; HTML&#xff1a; <div class"container"><div class"left"></div><div class"center"></div><div class"right">…

001集:open语句打开文件及文件类型(二进制、文本文件)详解——vba

open用法可以用来打开文件、文件夹或网页&#xff0c;也可以用来运行某一应用程序、文件或网页。一般来说&#xff0c;只要在开始菜单中可以找到某个应用程序&#xff0c;我们就可以使用open命令打开该应用程序;另外&#xff0c;在打开某个文件或网页时&#xff0c;也可以使用o…

设计模式学习笔记05(小滴课堂)

讲解Adapeter设计模式和应用场景 接口的适配器案例实战 代码&#xff1a; 定义一个接口&#xff1a; 编写适配器&#xff1a; 写我们的商品类&#xff1a; 会员类&#xff1a; 这样我们不同的需求可以根据需要去实现不同的接口方法&#xff0c;而不用实现全部接口方法。 适配…

Qt案例 在对QGraphicsView视图修改和撤销修改图元操作时,使用命令模式实现。

当项目中有QGraphicsView视图直接修改图元的功能时&#xff0c;常会有CtriZ和CtrlY这种执行与撤销图元修改的功能&#xff0c;以便于在修改图元后能够进行一个还原/执行操作&#xff0c;此时就适合使用命令模式设计来实现这个功能。 以下示例在WINDOWS系统&#xff0c;Qt Creat…

echarts step line

https://ppchart.com/#/ <template><div class"c-box" ref"jsEchart"></div> </template><script> import * as $echarts from echarts // 事件处理函数 export default {props: {// 需要传递的数据data: {type: Array,defa…

字符串匹配算法(z函数模版)来自灵神。

一个字符串s求出s的z[i]&#xff0c;z[i]表示以s[i:n]这一段和s[0:n]的从前往后的连续相等字母个数。 比如 abacaba,z[2] (acaba与abacaba比较) 1。

SpringBoot整合Flowable最新教程(一)Flowable介绍

一、Flowable 入门介绍 代码实现文章&#xff1a;SpringBoot整合Flowable最新教程&#xff08;二&#xff09; 官网地址&#xff1a;https://www.flowable.org/   Flowable6.3中文教程&#xff1a;中文教程地址   可以在官网下载对应的jar包在本地部署运行&#xff0c;官方…