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);
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.运行测试

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


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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenHarmony应用构建工具Hvigor的构建流程

前言 OpenHarmony 应用和服务使用 Hvigor 作为工程的构建工具。本篇文章将介绍 Hvigor 的构建流程&#xff0c;通过修改脚本配置使 Hvigor 执行自定义任务。 Hvigor 的构建流程 加载命令行参数和环境变量&#xff1b;初始化项目结构&#xff0c;创建 Project 和 Module 实例…

OpenCV | 光流估计

光流估计 光流是空间运动物体在观测成像平面上的像素运动的“瞬时速度”&#xff0c;根据各个像素点的速度的速度矢量特征&#xff0c;可以对图像进行动态分析&#xff0c;例如目标跟踪 高度恒定&#xff1a;同一点随着时间的变化&#xff0c;其亮度不会发生改变。小运动&…

WEB 3D技术 three.js 顶点交换

本文 我们来说 顶点的转换 其实就是 我们所有顶点的位置发生转变 我们整个物体的位置也会随之转变 这里 我们编写代码如下 import ./style.css import * as THREE from "three"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.j…

在Raspberry Pi Zero W中配置TFT LCD Framebuffer驱动

TFT LCD Framebuffer驱动配置 文章目录 TFT LCD Framebuffer驱动配置1、硬件准备2、软件配置2.1 启用SPI驱动2.2 TFT LCD设备驱动树配置 本文将以ILI9341 LCD为例&#xff0c;将详细介绍如何配置TFT LCD的Framebuffer驱动。 1、硬件准备 Raspberry Pi Zero W开发板一个&#x…

密码学中的Hash函数

目录 一. 介绍 二. hash函数的五个基本性质 &#xff08;&#xff11;&#xff09;压缩性 &#xff08;&#xff12;&#xff09;正向计算简单性 &#xff08;&#xff13;&#xff09;逆向计算困难性 &#xff08;&#xff14;&#xff09;弱无碰撞性 &#xff08;&…

(九)One-Wire总线-DS18B20

文章目录 One-Wire总线篇复位和应答读/写0&#xff0c;1 DS18B20篇原理图概述最主要特性几个重要的寄存器&#xff08;部分要掌握&#xff09;存储有数字温度结果的2个字节宽度的温度寄存器寄存器描述&#xff1a;寄存器说明&#xff1a; 一个字节的过温和一个字节的低温&#…

[嵌入式AI从0开始到入土]10_yolov5在昇腾上应用

[嵌入式AI从0开始到入土]嵌入式AI系列教程 注&#xff1a;等我摸完鱼再把链接补上 可以关注我的B站号工具人呵呵的个人空间&#xff0c;后期会考虑出视频教程&#xff0c;务必催更&#xff0c;以防我变身鸽王。 第一章 昇腾Altas 200 DK上手 第二章 下载昇腾案例并运行 第三章…

window使用cpolar实现内网穿透

文章目录 cpolar下载和安装启动和配置cpolar卸载 cpolar下载和安装 进入spolar官网&#xff0c;完成注册&#xff0c;下载相应的cploar版本解压和运行安装文件 配置安装路径&#xff0c;然后选择next&#xff0c;完成即可 启动和配置 点击首页的快捷图标打开网页&#xf…

python学习:实现猜数游戏和汉诺塔问题的解决

实现猜数游戏 规则&#xff1a; 计算机随机产生一个0~100的预设数字&#xff0c;让用户通过键盘输入所猜的数&#xff0c;如果大于预设的数&#xff0c;显示“遗憾&#xff0c;太大了“&#xff1b;小于预设的数&#xff0c;显示”遗憾&#xff0c;太小了“&#xff0c;如此循…

【MySQL】数据库之MMM高可用

目录 一、什么是MMM 二、关于MMM架构的说明 三、实操MMM的高可用 步骤一&#xff1a;完成主主复制、主从复制 步骤二&#xff1a;所有节点服务器都安装mysql-mmm,并完成mmm_common.conf文件的配置 步骤三&#xff1a;完成monitor节点服务器的配置文件修改mmm_mon.conf 步…

基于SSM的基金投资交易管理网站的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

K210基础实验系列

CanMV K210 开发板: CanMV K210 是由 01Studio 设计研发&#xff0c;基于嘉楠科技边缘计算芯片 K210 &#xff08; RSIC V 架构&#xff0c; 64 位双核&#xff09;方案的一款开发板&#xff0c;采用硬件一体化设计&#xff08; K210 核心板、 摄像头、 LCD 集成在一个…

mysql进阶-重构表

目录 1. 原因 2. 如何重构表呢&#xff1f; 2.1 命令1&#xff1a; 2.2 命令2&#xff1a; 2.3 命令3&#xff1a; 1. 原因 正常的业务开发&#xff0c;为什么需要重构表呢&#xff1f; 原因1&#xff1a;某张表存在大量的新增和删除操作&#xff0c;导致表经历过大量的…

深入了解Snowflake雪花算法:分布式唯一ID生成器

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

JVM中对象的创建

一.JVM运行流程 JVM向操作系统申请内存&#xff0c;初始化运行时数据区&#xff0c;接下来装载使用的类&#xff0c;执行类里面相应方法的时候为当前虚拟机栈压入一个栈帧&#xff0c;方法执行完成后栈帧出栈&#xff0c;进行垃圾回收。 二.JVM中对象的创建过程 符号引用&…

MySQL数据库进阶-事务

事务 事务由单独单元的一个或多个SQL语句组成&#xff0c;在这 个单元中&#xff0c;每个MySQL语句是相互依赖的。而整个单独单 元作为一个不可分割的整体&#xff0c;如果单元中某条SQL语句一 旦执行失败或产生错误&#xff0c;整个单元将会回滚。所有受到影 响的数据将返回到…

端云协同,Akamai 与快手联合落地 QUIC 提升海外用户视频体验

10月10日&#xff0c;负责支持和保护数字化体验且深受全球企业信赖的解决方案提供商阿卡迈技术公司( Akamai Technologies, Inc.&#xff0c;以下简称&#xff1a;Akamai )( NASDAQ&#xff1a;AKAM )携手全球领先的短视频记录和分享平台快手(HK&#xff1a;1024)通过全面落地 …

【LeetCode】150. 逆波兰表达式求值(ASCII码)

今日学习的文章链接和视频链接 leetcode题目地址&#xff1a;150. 逆波兰表达式求值 代码随想录题解地址&#xff1a;代码随想录 题目简介 即将后缀表达式转换成中缀表达式并计算。 给你一个字符串数组 tokens &#xff0c;表示一个根据 逆波兰表示法 表示的算术表达式。 …

【无线通信专题】NFC通信模式及可能的应用方式

在文章【无线通信专题】NFC基本原理中我们讲到了NFC工作模式。其中NFC工作模式主要有三种,读写模式、卡模拟模式、点对点模式。 NFC通信模式丰富,NFC Forum定义了三种NFC设备:通用NFCForum设备、读写器设备和标签设备。这些NFC设备可以在三种通信模式下运行,并对应用案例进…

【DevOps-05】Integrate工具

一、简要说明 持续集成、持续部署的工具很多,其中Jenkins是一个开源的持续集成平台。 Jenkins涉及到将编写完毕的代码发布到测试环境和生产环境的任务,并且还涉及到了构建项目等任务。 Jenkins需要大量的插件保证工作,安装成本较高,下面会基于Docker搭建Jenkins。 二、Jenk…