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

news2024/11/26 23:20:21

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


        本章学习Linux三大驱动之一的块设备驱动,主要应用场景为存储设备。

        本章的思维导图如下:

 一、什么是块设备

        块设备---存储设备

        以块为单位进行读写访问

        在机构上可以进行随机访问,使用缓冲区来暂时存放数据。

        I/O算法也不同。

二、块设备驱动框架

1.block_device结构体

1 struct block_device {
2 dev_t bd_dev; /* not a kdev_t - it's a search key */
3 int bd_openers;
4 struct inode *bd_inode; /* will die */
5 struct super_block *bd_super;
6 struct mutex bd_mutex; /* open/close mutex */
7 struct list_head bd_inodes;
8 void * bd_claiming;
9 void * bd_holder;
10 int bd_holders;
11 bool bd_write_holder;
12 #ifdef CONFIG_SYSFS
13 struct list_head bd_holder_disks;
14 #endif
15 struct block_device *bd_contains;
16 unsigned bd_block_size;
17 struct hd_struct *bd_part;
18 /*number of times partitions within this device have been opened.*/
19 unsigned bd_part_count;
20 int bd_invalidated;
21 struct gendisk *bd_disk;/*bd_disk 成员变量,此成员变量为
gendisk 结构体指针类型。内核使用 block_device 来表示一个具体的块设备对象,比如一个硬盘
或者分区,如果是硬盘的话 bd_disk 就指向通用磁盘结构 gendisk。*/
22 struct request_queue *bd_queue;
23 struct list_head bd_list;
24 /*
25 * Private data. You must have bd_claim'ed the block_device
26 * to use this. NOTE: bd_claim allows an owner to claim
27 * the same device multiple times, the owner must take special
28 * care to not mess up bd_private for that case.
29 */
30 unsigned long bd_private;
31
32 /* The counter of freeze processes */
33 int bd_fsfreeze_count;
34 /* Mutex for freeze */
35 struct mutex bd_fsfreeze_mutex;
36 };

1).注册块设备

int register_blkdev(unsigned int major, const char *name)
/*
major: 主设备号。
name: 块设备名字。
返回值: 如果参数 major 在 1~255 之间的话表示自定义主设备号,那么返回 0 表示注册成
功,如果返回负值的话表示注册失败。如果 major 为 0 的话表示由系统自动分配主设备号,那
么返回值就是系统分配的主设备号(1~255),如果返回负值那就表示注册失败。
*/

2).注销块设备

void unregister_blkdev(unsigned int major, const char *name)
/*

major: 要注销的块设备主设备号。
name: 要注销的块设备名字。
返回值: 无。
*/

 2.gendisk结构体

1 struct gendisk {
2 /* major, first_minor and minors are input parameters only,
3 * don't use directly. Use disk_devt() and disk_max_parts().
4 */
5 int major; /* major number of driver *///major 为磁盘设备的主设备号。
6 int first_minor;//first_minor 为磁盘的第一个次设备号。
7 int minors; /* maximum number of minors, =1 for
8 * disks that can't be partitioned. *///minors 为磁盘的次设备号数量,也就是磁盘的分区数
//量,这些分区的主设备号一样, 次设备号不同。
9
10 char disk_name[DISK_NAME_LEN]; /* name of major driver */
11 char *(*devnode)(struct gendisk *gd, umode_t *mode);
12
13 unsigned int events; /* supported events */
14 unsigned int async_events; /* async events, subset of all */
15
16 /* Array of pointers to partitions indexed by partno.
17 * Protected with matching bdev lock but stat and other
18 * non-critical accesses use RCU. Always access through
19 * helpers.
20 */
21 struct disk_part_tbl __rcu *part_tbl;/*
part_tbl 为磁盘对应的分区表,为结构体 disk_part_tbl 类型, disk_part_tbl 的核心
是一个 hd_struct 结构体指针数组,此数组每一项都对应一个分区信息。
*/
22 struct hd_struct part0;
23
24 const struct block_device_operations *fops;/*
fops 为块设备操作集,为 block_device_operations 结构体类型。和字符设备操作
集 file_operations 一样,是块设备驱动中的重点!
*/
25 struct request_queue *queue;/*
queue 为磁盘对应的请求队列,所以针对该磁盘设备的请求都放到此队列中,驱
动程序需要处理此队列中的所有请求。
*/
26 void *private_data;
27
28 int flags;
29 struct device *driverfs_dev; // FIXME: remove
30 struct kobject *slave_dir;
31
32 struct timer_rand_state *random;
33 atomic_t sync_io; /* RAID */
34 struct disk_events *ev;
35 #ifdef CONFIG_BLK_DEV_INTEGRITY
36 struct blk_integrity *integrity;
37 #endif
38 int node_id;
39 };

 gendisk 操作函数:

申请 gendisk:

struct gendisk *alloc_disk(int minors)
函数参数和返回值含义如下:
minors: 次设备号数量, 也就是 gendisk 对应的分区数量。
返回值: 成功:返回申请到的 gendisk,失败: NULL。

删除gendisk:

void del_gendisk(struct gendisk *gp)
函数参数和返回值含义如下:
gp: 要删除的 gendisk。
返回值: 无。

将 gendisk 添加到内核:

void add_disk(struct gendisk *disk)
函数参数和返回值含义如下:
disk: 要添加到内核的 gendisk。
返回值: 无。

设置 gendisk 容量:

void set_capacity(struct gendisk *disk, sector_t size)
函数参数和返回值含义如下:
disk: 要设置容量的 gendisk。
size: 磁盘容量大小,注意这里是扇区数量。

调整 gendisk 引用计数:get_disk 是增加 gendisk 的引用计数, put_disk 是减少 gendisk 的引用计数。

truct kobject *get_disk(struct gendisk *disk)
void put_disk(struct gendisk *disk)

3.block_device_operation结构体

1 struct block_device_operations {
2 int (*open) (struct block_device *, fmode_t);/*open 函数用于打开指定的块设备*/
3 void (*release) (struct gendisk *, fmode_t);/*release 函数用于关闭(释放)指定的块设备。*/
4 int (*rw_page)(struct block_device *, sector_t, struct page *,
int rw);/*rw_page 函数用于读写指定的页。*/
5 int (*ioctl) (struct block_device *, fmode_t, unsigned,
unsigned long);/*ioctl 函数用于块设备的 I/O 控制。*/
6 int (*compat_ioctl) (struct block_device *, fmode_t, unsigned,
unsigned long);/*
compat_ioctl 函数和 ioctl 函数一样,都是用于块设备的 I/O 控制。区别在于在 64
位系统上, 32 位应用程序的 ioctl 会调用 compat_iotl 函数。在 32 位系统上运行的 32 位应用程
序调用的就是 ioctl 函数。*/
7 long (*direct_access)(struct block_device *, sector_t,
8 void **, unsigned long *pfn, long size);
9 unsigned int (*check_events) (struct gendisk *disk,
10 unsigned int clearing);
11 /* ->media_changed() is DEPRECATED, use ->check_events() instead */
12 int (*media_changed) (struct gendisk *);
13 void (*unlock_native_capacity) (struct gendisk *);
14 int (*revalidate_disk) (struct gendisk *);
15 int (*getgeo)(struct block_device *, struct hd_geometry *);/*getgeo 函数用于获取磁盘信息,包括磁头、柱面和扇区等信息。*/
16 /* this callback is with swap_lock and sometimes page table lock
held */
17 void (*swap_slot_free_notify) (struct block_device *,
unsigned long);
18 struct module *owner;/*owner 表示此结构体属于哪个模块,一般直接设置为 THIS_MODULE。*/
19 };

4.块设备I/O请求过程

1)、请求队列 request_queue

初始化请求队列:

request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)
函数参数和返回值含义如下:
rfn: 请求处理函数指针,每个 request_queue 都要有一个请求处理函数,请求处理函数
request_fn_proc 原型如下:
void (request_fn_proc) (struct request_queue *q)
请求处理函数需要驱动编写人员自行实现。
lock: 自旋锁指针,需要驱动编写人员定义一个自旋锁,然后传递进来。,请求队列会使用
这个自旋锁。
返回值: 如果为 NULL 的话表示失败,成功的话就返回申请到的 request_queue 地址。

删除请求队列:

oid blk_cleanup_queue(struct request_queue *q)
函数参数和返回值含义如下:
q: 需要删除的请求队列。
返回值: 无。

分配请求队列并绑定制造请求函数:
 

struct request_queue *blk_alloc_queue(gfp_t gfp_mask)
函数参数和返回值含义如下:
gfp_mask: 内存分配掩码,具体可选择的掩码值请参考 include/linux/gfp.h 中的相关宏定义,
一般为 GFP_KERNEL。
返回值: 申请到的无 I/O 调度的 request_queue。
void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn)
函数参数和返回值含义如下:
q: 需要绑定的请求队列,也就是 blk_alloc_queue 申请到的请求队列。
mfn:需要绑定的“制造”请求函数,函数原型如下:
void (make_request_fn) (struct request_queue *q, struct bio *bio)
“制造请求”函数需要驱动编写人员实现。
返回值: 无。

         一般 blk_alloc_queue 和 blk_queue_make_request 是搭配在一起使用的,用于那么非机械的存储设备、无需 I/O 调度器,比如 EMMC、 SD 卡等。 blk_init_queue 函数会给请求队列分配一个 I/O 调度器,用于机械存储设备,比如机械硬盘等。

2)、请求request

获取请求:

request *blk_peek_request(struct request_queue *q)
函数参数和返回值含义如下:
q: 指定 request_queue。
返回值: request_queue 中下一个要处理的请求(request),如果没有要处理的请求就返回
NULL。

开启请求:

void blk_start_request(struct request *req)
函数参数和返回值含义如下:
req: 要开始处理的请求。
返回值: 无。

 一步到位处理请求:

1 struct request *blk_fetch_request(struct request_queue *q)
2 {
3 struct request *rq;
4 5
rq = blk_peek_request(q);
6 if (rq)
7 blk_start_request(rq);
8 return rq;
9 }

其他和请求有关的函数:

3)、bio结构

        bio 结构描述了要读写的起始扇区、要读写的扇区数量、是读取还是写入、页偏移、数据长度等等信息。
         bio_vec 就是“page,offset,len”组合, page 指定了所在的物理页, offset 表示所处页的偏移地址, len 就是数据长度。

遍历请求中的bio:

#define __rq_for_each_bio(_bio, rq) \
if ((rq->bio)) \
for (_bio = (rq)->bio; _bio; _bio = _bio->bi_next)
_bio 就是遍历出来的每个 bio, rq 是要进行遍历操作的请求, _bio 参数为 bio 结构体指针类
型, rq 参数为 request 结构体指针类型。

遍历bio中的所有段:

#define bio_for_each_segment(bvl, bio, iter) \
__bio_for_each_segment(bvl, bio, iter, (bio)->bi_iter)
第一个 bvl 参数就是遍历出来的每个 bio_vec,第二个 bio 参数就是要遍历的 bio,类型为
bio 结构体指针,第三个 iter 参数保存要遍历的 bio 中 bi_iter 成员变量。

通知bio处理结束 : 

bvoid bio_endio(struct bio *bio, int error)
函数参数和返回值含义如下:
bio: 要结束的 bio。
error: 如果 bio 处理成功的话就直接填 0,如果失败的话就填个负值,比如-EIO。
返回值: 无

以下内容将在下一个笔记中学习:

三、使用请求队列实验

1.实验程序编写

2.运行测试

四、不使用请求队列实验

1.实验程序编写

2.运行测试


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

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

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

相关文章

经典目标检测YOLO系列(一)复现YOLOV1(3)正样本的匹配及损失函数的实现

经典目标检测YOLO系列(一)复现YOLOV1(3)正样本的匹配及损失函数的实现 之前,我们依据《YOLO目标检测》(ISBN:9787115627094)一书,提出了新的YOLOV1架构,并解决前向推理过程中的两个问题,继续按照此书进行YOLOV1的复现。 经典目标…

数据结构OJ实验8-赫夫曼树编码及应用

A. DS二叉树--赫夫曼树的构建与编码 题目描述 给定n个权值,根据这些权值构造huffman树,并进行huffman编码 大家参考课本算法6.12为主,注意数组访问是从位置1开始 要求:赫夫曼的构建中,默认左孩子权值不大于右孩子权…

webRTC实时通信demo

参考文档: https://www.jianshu.com/p/f439ce5cc0be https://www.w3cschool.cn/socket demo流程示意图(用户A向用户B推送视频): #mermaid-svg-0KZaDQ5DBl28zjmZ {font-family:"trebuchet ms",verdana,arial,sans-seri…

JavaWeb——前端之HTMLCSS

学习视频链接:https://www.bilibili.com/video/BV1m84y1w7Tb/?spm_id_from333.999.0.0 一、Web开发 1. 概述 能通过浏览器访问的网站 2. Web网站的开发模式——主流是前后端分离 二、前端Web开发 1. 初识 前端编写的代码通过浏览器进行解析和渲染得到我们看到…

elasticsearch+Kibana

什么是es(elasticsearch) Elasticsearch是一个开源的分布式搜索和分析引擎,它构建在Apache Lucene搜索引擎库之上。它提供了一个分布式多用户能力的实时搜索和分析引擎,能够处理大规模的数据。Elasticsearch被广泛用于构建全文搜索、日志分析、实时应用…

灸哥问答:软件架构在软件研发中的作用

软件架构在软件开发中扮演着至关重要的角色。我们在软件研发的过程中,类比于建造一座公寓楼,而软件架构就像是盖楼之前的设计图纸,如果没有设计图纸就直接盖楼,可想而知带来的后果是什么。我对软件架构的作用表现总结如下&#xf…

iOS问题记录 - iOS 17通过NSUserDefaults设置UserAgent无效(续)

文章目录 前言开发环境问题描述问题分析1. 准备源码2. 定位源码3. 对比源码4. 分析总结 解决方案补充内容1. UserAgent的组成2. UserAgent的设置优先级 最后 前言 在上篇文章中对该问题做了一些判断和猜测,并给出了解决方案。不过,美中不足的是没有进一…

十四:爬虫-Redis基础

1、背景 随着互联网大数据时代的来临,传统的关系型数据库已经不能满足中大型网站日益增长的访问量和数据量。这个时候就需要一种能够快速存取数据的组件来缓解数据库服务I/O的压力,来解决系统性能上的瓶颈。 2、redis是什么 Redis 全称 Remote Dictio…

C/C++面向对象(OOP)编程-回调函数详解(回调函数、C/C++异步回调、函数指针)

本文主要介绍回调函数的使用,包括函数指针、异步回调编程、主要通过详细的例子来指导在异步编程和事件编程中如何使用回调函数来实现。 🎬个人简介:一个全栈工程师的升级之路! 📋个人专栏:C/C精进之路 &…

【Spring实战】16 Profile

文章目录 1. 定义2. 使用2.1 定义 Profile2.2 激活 Profile 3. 演示3.1 properties文件3.2 打印日志3.3 启动服务&验证3.4 修改 active3.5 重启服务&验证 4. 应用场景4.1 数据库配置4.2 日志配置 5. 代码详细总结 Spring 框架提供了一种强大的机制,允许在不…

图像分割实战-系列教程9:U2NET显著性检测实战1

🍁🍁🍁图像分割实战-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 U2NET显著性检测实战1 1、任务概述

第7课 利用FFmpeg将摄像头画面与麦克风数据合成后推送到rtmp服务器

上节课我们已经拿到了摄像头数据和麦克风数据,这节课我们来看一下如何将二者合并起来推送到rtmp服务器。推送音视频合成流到rtmp服务器地址的流程如下: 1.创建输出流 //初始化输出流上下文 avformat_alloc_output_context2(&outFormatCtx, NULL, &…

Java EE Servlet之Cookie 和 Session

文章目录 1. Cookie 和 Session1.1 Cookie1.2 理解会话机制 (Session)1.2.1 核心方法 2. 用户登录2.1 准备工作2.2 登录页面2.3 写一个 Servlet 处理上述登录请求2.4 实现登录后的主页 3. 总结 1. Cookie 和 Session 1.1 Cookie cookie 是 http 请求 header 中的一个属性 浏…

AI 工具探索(二)

我参加了 奇想星球 与 Datawhale 举办的 【AI办公 X 财务】第一期,现在这是第二次打卡,也即自由探索,我选择 Modelscope 的 Agent 探索,并用gpts创作助理对比! 最近想学学小红书的运营方法,选择了 小红书I…

【微服务】1.虚拟机配置

创建虚拟机选经典,其他配置同其他讲解文档 特殊注意 如果要自己设置IP地址,修改/etc/sysconfig/network-scripts/ 编辑ifcfg-ens33需改ip地址 #开机加载网络配置启动网络服务 ONBOOT"yes" #分配ip的协议 none static :不自动分配&#xff0c…

axios的使用及说明

目录 1.说明 2.直接使用 3.封装使用 4.注意 1.说明 官网:Axios 实例 | Axios中文文档 | Axios中文网 Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使…

FL Studio 21最新版本for mac 21.2.2.3740中文解锁版2024最新图文安装教程

FL Studio 21最新版本for mac 21.2.0.3740中文解锁版是最新强大的音乐制作工具。它可以与所有类型的音乐一起创作出令人惊叹的音乐。它提供了一个非常简单且用户友好的集成开发环境(IDE)来工作。这个完整的音乐工作站是由比利时公司 Image-Line 开发的。…

redis容灾的方案设计

背景 今年各个大厂的机房事故频繁,其中关键组件Redis是重灾区,本文就来看下怎么做Redis的多机房容灾 Redis多机房容灾方案 1.首先最最直观的是直接利用Redis内部的主从数据同步来进行灾备,但是由于Redis内部的主从实现对机房间的网络延迟等…

2024 React 后台系统 搭建学习看这一篇就够了(1)

年初,自己想写一篇关于 React 实战后台项目的 课程文章,也算是对自己 2023的前端学习做一个系统性总结,方便后续查阅,也方便自己浏览,还能增加自己的文笔 网上很多平台都不太稳定,所以用了阿里的语雀&…

声明式导航传参详情

1 动态路由传参 路由规则path ->/article/:aid 导航链接 <router-link to"/article/1">查看第一篇文章</router-link> 组件获取参数: this.$route.params.aid 如果想要所有的值&#xff0c;就用this. $route. params 注意&#xff1a;这两个必须匹配…