Linux学习第50天:Linux块设备驱动实验(二):Linux三大驱动之一

Linux版本号4.1.15   芯片I.MX6ULL                                    大叔学Linux    品人间百味  思文短情长 


三、使用请求队列实验

1.实验程序编写

        使用开发板上的一段RAM来模拟一段块设备,也就是ramdisk.

        机械硬盘

34 #define RAMDISK_SIZE (2 * 1024 * 1024) /* 容量大小为 2MB */
35 #define RAMDISK_NAME "ramdisk" /* 名字 */
36 #define RADMISK_MINOR 3 /* 表示三个磁盘分区!不是次设备号为 3! */
37
38 /* ramdisk 设备结构体 */
39 struct ramdisk_dev{
40 int major; /* 主设备号 */
41 unsigned char *ramdiskbuf; /* ramdisk 内存空间,用于模拟块设备 */
42 spinlock_t lock; /* 自旋锁 */
43 struct gendisk *gendisk; /* gendisk */
44 struct request_queue *queue;/* 请求队列 */
45 };
46
47 struct ramdisk_dev ramdisk; /* ramdisk 设备 */

驱动模块的加载与卸载:

6 static int __init ramdisk_init(void)
7 {
8 int ret = 0;
9
10 /* 1、申请用于 ramdisk 内存 */
11 ramdisk.ramdiskbuf = kzalloc(RAMDISK_SIZE, GFP_KERNEL);
12 if(ramdisk.ramdiskbuf == NULL) {
13 ret = -EINVAL;
14 goto ram_fail;
15 }
16
17 /* 2、初始化自旋锁 */
18 spin_lock_init(&ramdisk.lock);
19
20 /* 3、注册块设备 */
21 ramdisk.major = register_blkdev(0, RAMDISK_NAME); /* 自动分配 */
22 if(ramdisk.major < 0) {
23 goto register_blkdev_fail;
24 }
25 printk("ramdisk major = %d\r\n", ramdisk.major);
26
27 /* 4、分配并初始化 gendisk */
28 ramdisk.gendisk = alloc_disk(RADMISK_MINOR);
29 if(!ramdisk.gendisk) {
30 ret = -EINVAL;
31 goto gendisk_alloc_fail;
32 }
33
34 /* 5、分配并初始化请求队列 */
35 ramdisk.queue = blk_init_queue(ramdisk_request_fn,
&ramdisk.lock);
36 if(!ramdisk.queue) {
37 ret = EINVAL;
38 goto blk_init_fail;
39 }
40
41 /* 6、添加(注册)disk */
42 ramdisk.gendisk->major = ramdisk.major; /* 主设备号 */
43 ramdisk.gendisk->first_minor = 0; /*起始次设备号) */
44 ramdisk.gendisk->fops = &ramdisk_fops; /* 操作函数 */
45 ramdisk.gendisk->private_data = &ramdisk; /* 私有数据 */
46 ramdisk.gendisk->queue = ramdisk.queue; /* 请求队列 */
47 sprintf(ramdisk.gendisk->disk_name, RAMDISK_NAME);/* 名字 */
48 set_capacity(ramdisk.gendisk, RAMDISK_SIZE/512); /* 设备容量(单位
为扇区)*/
49 add_disk(ramdisk.gendisk);
50
51 return 0;
52
53 blk_init_fail:
54 put_disk(ramdisk.gendisk);
55 gendisk_alloc_fail:
56 unregister_blkdev(ramdisk.major, RAMDISK_NAME);
57 register_blkdev_fail:
58 kfree(ramdisk.ramdiskbuf); /* 释放内存 */
59 ram_fail:
60 return ret;
61 }
62
63 /*
64 * @description : 驱动出口函数
65 * @param : 无
66 * @return : 无
67 */
68 static void __exit ramdisk_exit(void)
69 {
70 /* 释放 gendisk */
71 put_disk(ramdisk.gendisk);
72 del_gendisk(ramdisk.gendisk);
73
74 /* 清除请求队列 */
75 blk_cleanup_queue(ramdisk.queue);
7677 /* 注销块设备 */
78 unregister_blkdev(ramdisk.major, RAMDISK_NAME);
79
80 /* 释放内存 */
81 kfree(ramdisk.ramdiskbuf);
82 }
83
84 module_init(ramdisk_init);
85 module_exit(ramdisk_exit);
86 MODULE_LICENSE("GPL");
87 MODULE_AUTHOR("zuozhongkai");

 块设备的操作集:

1 /*
2 * @description : 打开块设备
3 * @param - dev : 块设备
4 * @param - mode : 打开模式
5 * @return : 0 成功;其他 失败
6 */
7 int ramdisk_open(struct block_device *dev, fmode_t mode)
8 {
9 printk("ramdisk open\r\n");
10 return 0;
11 }
12
13 /*
14 * @description : 释放块设备
15 * @param - disk : gendisk
16 * @param - mode : 模式
17 * @return : 0 成功;其他 失败
18 */
19 void ramdisk_release(struct gendisk *disk, fmode_t mode)
20 {
21 printk("ramdisk release\r\n");
22 }
23
24 /*
25 * @description : 获取磁盘信息
26 * @param - dev : 块设备
27 * @param - geo : 模式
28 * @return : 0 成功;其他 失败
29 */
30 int ramdisk_getgeo(struct block_device *dev,
struct hd_geometry *geo)
31 {
32 /* 这是相对于机械硬盘的概念 */
33 geo->heads = 2; /* 磁头 */
34 geo->cylinders = 32; /* 柱面 */
35 geo->sectors = RAMDISK_SIZE / (2 * 32 *512); /* 磁道上的扇区数量 */
36 return 0;
37 }
38
39 /*
40 * 块设备操作函数
41 */
42 static struct block_device_operations ramdisk_fops =
43 {
44 .owner = THIS_MODULE,
45 .open = ramdisk_open,
46 .release = ramdisk_release,
47 .getgeo = ramdisk_getgeo,
48 };

getgeo函数获取磁盘信息,信息保存在参数geo中,为结构体hd_geometry类型: 

1 struct hd_geometry {
2 unsigned char heads; /* 磁头 */
3 unsigned char sectors; /*一个磁道上的扇区数量 */
4 unsigned short cylinders; /* 柱面 */
5 unsigned long start;
6 };

请求处理函数:从块设备中读取数据,或向块设备中写入数据。

1 /*
2 * @description : 处理传输过程
3 * @param-req : 请求
4 * @return : 无
5 */
6 static void ramdisk_transfer(struct request *req)
7 {
8 unsigned long start = blk_rq_pos(req) << 9; /* blk_rq_pos 获取到的是
扇区地址,左移 9 位转换为字节地址 */
9 unsigned long len = blk_rq_cur_bytes(req); /* 大小 */
10
11 /* bio 中的数据缓冲区
12 * 读:从磁盘读取到的数据存放到 buffer 中
13 * 写: buffer 保存这要写入磁盘的数据
14 */
15 void *buffer = bio_data(req->bio);
16
17 if(rq_data_dir(req) == READ) /* 读数据 */
18 memcpy(buffer, ramdisk.ramdiskbuf + start, len);
19 else if(rq_data_dir(req) == WRITE) /* 写数据 */
20 memcpy(ramdisk.ramdiskbuf + start, buffer, len);
21
22 }
23
24 /*
25 * @description : 请求处理函数
26 * @param-q : 请求队列
27 * @return : 无
28 */
29 void ramdisk_request_fn(struct request_queue *q)
30 {
31 int err = 0;
32 struct request *req;
33
34 /* 循环处理请求队列中的每个请求 */
35 req = blk_fetch_request(q);
36 while(req != NULL) {
37
38 /* 针对请求做具体的传输处理 */
39 ramdisk_transfer(req);
40
41 /* 判断是否为最后一个请求,如果不是的话就获取下一个请求
42 * 循环处理完请求队列中的所有请求。
43 */
44 if (!__blk_end_request_cur(req, err))
45 req = blk_fetch_request(q);
46 }
47 }

 2.运行测试

        输入如下命令加载 ramdisk.ko 这个驱动模块。

depmod //第一次加载驱动的时候需要运行此命令
modprobe ramdisk.ko //加载驱动模块

参看ramdisk磁盘:

fdisk -l //查看磁盘信息

        格式化/dev/ramdisk:

mkfs.vfat /dev/ramdisk

        格式化以后就可以挂载访问:

mount /dev/ramdisk /tmp

四、不使用请求队列实验

1.实验程序编写

        ramdisk_init 函数:

1 static int __init ramdisk_init(void)
2 {
......
29
30 /* 5、分配请求队列 */
31 ramdisk.queue = blk_alloc_queue(GFP_KERNEL);
32 if(!ramdisk.queue){
33 ret = -EINVAL;
34 goto blk_allo_fail;
35 }
36
37 /* 6、设置“制造请求”函数 */
38 blk_queue_make_request(ramdisk.queue, ramdisk_make_request_fn);
39
40 /* 7、添加(注册)disk */
41 ramdisk.gendisk->major = ramdisk.major; /* 主设备号 */
42 ramdisk.gendisk->first_minor = 0; /* 起始次设备号 */
43 ramdisk.gendisk->fops = &ramdisk_fops; /* 操作函数 */
44 ramdisk.gendisk->private_data = &ramdisk; /* 私有数据 */
45 ramdisk.gendisk->queue = ramdisk.queue; /* 请求队列 */
46 sprintf(ramdisk.gendisk->disk_name, RAMDISK_NAME); /* 名字 */
47 set_capacity(ramdisk.gendisk, RAMDISK_SIZE/512); /* 设备容量*/
48 add_disk(ramdisk.gendisk);
49
......
60 return ret;
61 }

        “制造请求”函数 ramdisk_make_request_fn:ramdisk_make_request_fn函数里面是全部是对 bio 的操作:

1 /*
2 * @description : “制造请求”函数
3 * @param-q : 请求队列
4 * @return : 无
5 */
6 void ramdisk_make_request_fn(struct request_queue *q,
struct bio *bio)
7 {
8 int offset;
9 struct bio_vec bvec;
10 struct bvec_iter iter;
11 unsigned long len = 0;
12
13 offset = (bio->bi_iter.bi_sector) << 9; /* 获取设备的偏移地址 */
//直接读取 bio 的 bi_iter 成员变量的 bi_sector 来获取要操作的设备地址(扇区)。
14
15 /* 处理 bio 中的每个段 */
16 bio_for_each_segment(bvec, bio, iter){/*使用 bio_for_each_segment 函数循环获取 bio 中的每个段,然后对其每个段进
行处理。*/
17 char *ptr = page_address(bvec.bv_page) + bvec.bv_offset;/*根据 bio_vec 中页地址以及偏移地址转换为真正的数据起始地址。*/
18 len = bvec.bv_len;/*获取要处理的数据长度,也就是 bio_vec 的 bv_len 成员变量。*/
19
20 if(bio_data_dir(bio) == READ) /* 读数据 */
21 memcpy(ptr, ramdisk.ramdiskbuf + offset, len);
22 else if(bio_data_dir(bio) == WRITE) /* 写数据 */
23 memcpy(ramdisk.ramdiskbuf + offset, ptr, len);
24 offset += len;
25 }
26 set_bit(BIO_UPTODATE, &bio->bi_flags);
27 bio_endio(bio, 0);/*调用 bio_endio 函数,结束 bio。*/
28 }

2.运行测试

        测试方法和上一节笔记一样。


本笔记为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。

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

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

相关文章

使用即时设计绘制原型设计方便吗?和Axure RP相比怎么样?

对于原型设计&#xff0c;APP 和 Web 都是一样的&#xff0c;因为产品原型是用来确定需求的工具。我们使用这种工具的目的是为了快速迭代&#xff0c;从而深入挖掘和筛选产品的需求。 绘制原型&#xff0c;最重要的原则是&#xff1a;快速、清晰&#xff01; Axure 工具的优缺…

2023APMCM亚太数学建模C题 - 中国新能源汽车的发展趋势(3)

六、问题三的模型建立和求解 6.1问题分析 问题3.收集数据&#xff0c;建立数学模型分析新能源电动汽车对全球传统能源汽车行业的影响。 本题要求建立模型分析新能源电动汽车对全球传统能源汽车行业的影响。由于数据集可能略大&#xff0c;而在处理复杂问题、大量特征和大规模…

spatialRF

官网&#xff1a;Easy Spatial Modeling with Random Forest • spatialRF (blasbenito.github.io) spatialRF是一种在考虑空间自相关的前提下&#xff0c;利用随机森林对空间数据进行回归并解释的R包。 数据要求 参数命名 data&#xff1a;训练集&#xff0c;data frame。 …

基于SpringBoot的房屋租赁管理系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

WorkPlus Meet打造高质量的视频会议体验,助力实时远程协作

在全球化的商业环境中&#xff0c;远程协作和在线会议成为了企业高效工作的关键。作为一款高质量的视频会议软件&#xff0c;WorkPlus Meet以其卓越的性能和创新的功能&#xff0c;成为企业实时远程协作的首选。 WorkPlus Meet打造了高质量的视频会议体验&#xff0c;为企业提供…

书生·浦语大模型趣味 Demo笔记及作业

文章目录 笔记作业基础作业&#xff1a;进阶作业&#xff1a; 笔记 书生浦语大模型InternLM-Chat-7B 智能对话 Demo&#xff1a;https://blog.csdn.net/m0_49289284/article/details/135412067书生浦语大模型Lagent 智能体工具调用 Demo&#xff1a;https://blog.csdn.net/m0_…

雾天条件下 SLS 融合网络的三维目标检测

论文地址&#xff1a;3D Object Detection with SLS-Fusion Network in Foggy Weather Conditions 论文代码&#xff1a;https://github.com/maiminh1996/SLS-Fusion 论文摘要 摄像头或激光雷达&#xff08;光检测和测距&#xff09;等传感器的作用对于自动驾驶汽车的环境意识…

vue项目中的录屏插件recordrtc且带声音

vue项目中的录屏插件recordrtc且带声音 一、效果图二、安装插件三、直接上代码 一、效果图 其中窗口录屏不带声音&#xff0c;chrome标签和整个屏幕的录屏是带声音的 二、安装插件 npm i recordrtc 三、直接上代码 <template><div class"record-page">…

react-router-domV6.21.1版本结合ant design mobile的TabBar标签栏和Popup弹出层实现移动端路由配置

react-router-demo react-router-dom在V6版本之后更换了很多的API名称&#xff0c;在ant design mobile的TabBar配置中还是之前的旧版本&#xff0c;比如使用了switch组件等。我们在这里使用新版本的react-router-dom进行react移动端的配置 首先使用npm下载最新版的react-rout…

MYSQL双主节点–更换ip

MYSQL双主节点–更换ip 一、更换双主节点ip 1.停止mysql服务 #安装了supervisor supervisorctl stop mysql #未安装 systemctl stop mysqld2.修改网卡配置信息 注&#xff1a;ens33是网卡名称&#xff0c;可能网卡不叫ens33 vi /etc/sysconfig/network-scripts/ifcfg-ens333…

Mybatis入门源码二:sql执行

后面开始分析sql执行的源码流程也就是这一部分 一、factory.openSession() 重点关注configuration.newExecutor这个方法&#xff0c;获取事务处理器比较简单&#xff0c;就是获取一个jdbc的事务管理器。 这个方法通过传入的执行器类型来创建不同的执行器&#xff0c;有simp…

CNN——ResNet

深度残差网络&#xff08;Deep residual network, ResNet&#xff09;的提出是CNN图像史上的一件里程碑事件&#xff0c;并且让深度学习真正可以继续做下去&#xff0c;斩获2016 CVPR Best Paper。此外ResNet的作者都是中国人&#xff0c;一作何恺明。ResNet被提出以后很多的网…

Vue3+Pinia实现持久化动态主题切换

PC端主题切换大家都用过&#xff0c;下面用Vue3Pinia实现一下这个过程; 【源码地址】 1、准备工作 npm install pinia npm install pinia-plugin-persist2、基础配置 // main.js import { createApp } from vue import App from ./App.vue import bootstrap from "../bo…

红日靶场 4

靶场配置 ​ 733 x 668899 x 819 ​ ​ 733 x 6161466 x 1232 ​ ​ 733 x 6261449 x 1237 ​ ​ 733 x 6301450 x 1247 ​ IP 地址分配&#xff1a; Win7: 192.168.183.133(内网)Ubuntu: 192.168.183.134(内网) 192.168.120.137(外网)DC: 192.168.183.130(内网)Kali…

Python综合数据分析_根据订单求RFM值

文章目录 0.导入数据1.数据可视化2.数据清洗3.特征工程4.构建User用户表5.求R值6.求F值7.求M值 0.导入数据 import pandas as pd #导入Pandas df_sales pd.read_csv(订单.csv) #载入数据 df_sales.head() #显示头几行数据 1.数据可视化 import matplotlib.pyplot as plt #导…

js逆向第13例:猿人学第6题js混淆-回溯赛

文章目录 m是加密字符串怎么来的?浏览器环境检测本地运行的js代码任务六:采集全部5页的彩票数据,计算全部中奖的总金额(包含一、二、三等奖) 此题总体难度低于第5题,老规矩还是查看控制台请求地址https://match.yuanrenxue.cn/api/match/6?m=rPRDgpbV3Wd%252FyPfURQAkxK…

使用Vite创建vue3工程

介绍 使用Vite构建工具&#xff0c;创建Vue3工程 示例 第一步&#xff1a;执行创建项目的命令&#xff0c;study-front-vue3是项目名称 npm init vite-app study-front-vue3第二步&#xff1a;进入项目文件夹&#xff0c;执行命令&#xff0c;安装模块 cd study-front-vue…

使用qtquick调用python程序,pytorch

一. 内容简介 使用qtquick调用python程序 二. 软件环境 2.1vsCode 2.2Anaconda version: conda 22.9.0 2.3pytorch 安装pytorch(http://t.csdnimg.cn/GVP23) 2.4QT 5.14.1 新版QT6.4,&#xff0c;6.5在线安装经常失败&#xff0c;而5.9版本又无法编译64位程序&#xf…

程序员为什么不能一次把功能写好,是因为他不想吗

引言 交流一下为什么他做的功能这么多Bug 大家好&#xff0c;最近看到一个有趣的问题&#xff1a; 程序员为什么要不能一次性写好&#xff0c;需要一直改Bug&#xff1f; 在我看来&#xff0c;程序员也是人&#xff0c;并非机器。 拿这个问题去质问程序员&#xff0c;答案无…

Java学习,一文掌握Java之SpringBoot框架学习文集(3)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…