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

news2024/12/27 15:02:48

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);
76

77 /* 注销块设备 */
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.coloradmin.cn/o/1364294.html

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

相关文章

文章解读与仿真程序复现思路——中国电机工程学报EI\CSCD\北大核心《考虑系统调峰需求与光热电站收益平衡的储热容量优化配置》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主的专栏栏目《论文与完整程序》 这个标题表明研究的主题涉及到光热电站系统中的储热容量优化配置&#xff0c;而优化的目标是在系统中实现调峰需求并平衡光热电站的收益。让我们逐步解读这…

使用.Net nanoFramework为ESP32进行蓝牙配网

通过前面的介绍&#xff0c;我们已经学会了如何使用 .NET nanoFramework 为 ESP32 设备连接 Wi-Fi 网络。然而&#xff0c;在实际的物联网环境中&#xff0c;我们往往需要使用更便捷的式来满足配网需求。这篇文章将带你了解一些常见的配网方案&#xff0c;并以 ESP32 为例&…

数据结构入门到入土——链表(2)

目录 一&#xff0c;与链表相关的题目&#xff08;2&#xff09; 1.输入两个链表&#xff0c;找出它们的第一个公共节点 2.给定一个链表&#xff0c;判断链表中是否有环 3.给定一个链表&#xff0c;返回链表开始入环的第一个节点&#xff0c;若无则返回null 一&#xff0c;…

【好书推荐】ReactJS实践入门

作者简介 Chris Minnick是一位多产的作家、博主、培训师、演说家和Web工程师。他创立的WatzThis&#xff1f;公司&#xff0c;一直致力于寻找更好的方法向初学者教授计算机和编程技能。 Chris拥有超过25年的全栈开发经验&#xff0c;他也是一名教龄超过10年的培训师&#xff0c…

科学的摇篮 - 贝尔实验室

AT&T贝尔实验室&#xff08;AT&T Bell Laboratories&#xff09;是美国电信公司AT&T的研究与开发部门&#xff0c;成立于1925年。它在20世纪的许多年里一直是科学与技术创新的重要中心&#xff0c;做出了众多重大贡献&#xff0c;并为多项科技成就奠定了基础。以下…

Java Swing手搓坦克大战遇到的问题和思考

1.游戏中的坐标系颇为复杂 像素坐标系还有行列坐标&#xff0c;都要使用&#xff0c;这之间的互相转化使用也要注意 2.游戏中坦克拐弯的处理&#xff0c;非常重要 由于坦克中心点是要严格对齐到一条网格线&#xff0c;并沿着这条线前进的&#xff0c;如果拐弯不做处理&#…

动态卡尺胶路检测

动态卡尺胶路检测 1. 示例效果2. 代码 1. 示例效果 使用了三个卡尺工具、一个线段工具。这种方法可以检测胶路最常见的缺陷&#xff1a;断胶和胶宽等 2. 代码 #region namespace imports using System; using System.Collections; using System.Drawing; using System.IO; …

【开发日记】IDEA“找不到或无法加载主类”问题

记录一个研究了两个小时的“玄学”问题找不到或无法加载主类。 ​1、问题 使用IDEA启动SpringBoot项目显示找不到或无法加载主类。 2、解决经历 尝试了很多种解决方法都没有解决&#xff0c;下面是我网上查询后尝试的一些方法。这些方法我都没有解决问题&#xff0c;是因为…

双十一的祈祷【算法赛】

问题描述 双十一&#xff0c;不仅是购物狂欢节&#xff0c;更有 "光棍节" 之称。这源于 11:1111:11 由四个 11 构成&#xff0c;象征着单身。 作为大学生的小蓝也想经历甜甜的校园恋爱&#xff0c;于是他找到了爱神丘比特&#xff0c;向他祈祷能为自己带来一段邂逅…

微软开源时空预测Fost的使用和解读

一、引言 时空预测是指对未知系统状态在时间和空间上的预测&#xff0c;它是地球系统科学、交通运输、智慧城市等领域的重要技术和工具。时空预测的目的是利用历史数据和当前信息&#xff0c;通过建立时空依赖关系&#xff0c;来推断未来的变化趋势和可能的情景。时空预测的应…

《PySpark大数据分析实战》-24.数据可视化图表介绍

&#x1f4cb; 博主简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是wux_labs。&#x1f61c; 热衷于各种主流技术&#xff0c;热爱数据科学、机器学习、云计算、人工智能。 通过了TiDB数据库专员&#xff08;PCTA&#xff09;、TiDB数据库专家&#xff08;PCTP…

强化学习4——动态规划初探

动态规划具体指的是在某些复杂问题中&#xff0c;将问题转化为若干个子问题&#xff0c;并在求解每个子问题的过程中保存已经求解的结果&#xff0c;以便后续使用。实际上动态规划更像是一种通用的思路&#xff0c;而不是具体某个算法。 在强化学习中&#xff0c;被用于求解值函…

CAN总线基础详解以及stm32的CAN控制器

目录 CAN简介 CAN总线拓扑图 CAN总线特定 CAN应用场景 CAN的物理层 CAN的协议层 CAN数据帧介绍 CAN位时序介绍 数据同步过程 硬件同步 再同步 CAN总线仲裁 stm32的CAN控制器 CAN控制器介绍 CAN控制器模式 CAN控制器框图 接收过滤器 CAN控制器波特率计算 CAN相…

基于SSM的图书商城(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的图书商城&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring SpringMv…

libexif库介绍

libexif是一个用于解析、编辑和保存EXIF数据的库。它支持EXIF 2.1标准(以及2.2中的大多数)中描述的所有EXIF标签。它是用纯C语言编写的&#xff0c;不需要任何额外的库。源码地址&#xff1a;https://github.com/libexif/libexif &#xff0c;最新发布版本为0.6.24&#xff0c;…

topsis算法

TOPSIS &#xff08;Technique for Order Preference by Similarity to an Ideal Solution &#xff09;模型中文叫做“逼近理想解排序方法”&#xff0c;是根据评价对象与理想化目标的接近程度进行排序的方法&#xff0c;是一种距离综合评价方法。基本思路是通过假定正、负理想…

C#,数值计算,基础函数——任意位数π的数值算法源程序与数据可视化

对于数学常数 PI 后面位数的计算与追求&#xff0c;是数学家与计算机科学家们乐此不疲的游戏。 一、圆周率PI简史 圆周率&#xff08;Pi&#xff09;是圆的周长与直径的比值&#xff0c;一般用希腊字母π表示&#xff0c;是一个在数学及物理学中普遍存在的数学常数。π也等于圆…

关于图像分割任务中按照比例将数据集随机划分成训练集和测试集

1. 前言 之前写了分类和检测任务划分数据集的脚本&#xff0c;三大任务实现了俩&#xff0c;基于强迫症&#xff0c;也实现一下图像分割的划分脚本 分类划分数据&#xff1a;关于图像分类任务中划分数据集&#xff0c;并且生成分类类别的josn字典文件 检测划分数据&#xff…

如何计算ThreadLocal对象的hash值?【ThreadLocal技术】(含AtomicInteger的介绍)

如何计算ThreadLocal对象的hash值&#xff1f; 一、前置知识二、问题三、剖析源码&#xff1a;如何计算ThreadLocal对象的hash值&#xff1f;1、源码1.1 咱先得知道nextHashCode的起始值1.1.1 那就要先了解AtomicInteger创建AtomicInteger原子的增减操作原子的加法操作原子的获…

对Tor的去匿名化攻击的调查

文章信息 论文题目&#xff1a;De-Anonymisation Attacks on Tor: A Survey 期刊&#xff08;会议&#xff09;&#xff1a; IEEE Communications Surveys & Tutorials 时间&#xff1a;2021 级别&#xff1a;中科院1区&#xff08;IF&#xff1a;35.6&#xff09; 文章链…