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

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.mfbz.cn/a/284456.html

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

相关文章

张量操作与线性回归

一、张量的操作:拼接、切分、索引和变换 (1)张量拼接与切分 1.1 torch.cat() 功能:将张量按维度dim进行拼接 • tensors: 张量序列 • dim : 要拼接的维度 torch.cat(tensors, dim0, outNone)函数用于沿着指定维度dim将多个张量…

数据结构模拟实现LinkedList双向不循环链表

目录 一、双向不循环链表的概念 二、链表的接口 三、链表的方法实现 (1)display方法 (2)size方法 (3)contains方法 (4)addFirst方法 (5)addLast方法 …

Java API 操作Docker浅谈

背景: 使用com.github.docker-java库可以很方便地在Java中操作Docker。下面是一个详细的教程,包括创建镜像、创建容器、启动容器、停止容器和删除容器的步骤以及每一步的说明。 前提: 首先,在你的Java项目中添加com.github.doc…

三、Mysql安全性操作[用户创建、权限分配]

一、用户 1.创建用户 CREATE USER test1localhost identified BY test1;2.删除用户 DROP USER test2localhost;二、权限分配 1.查询用户权限 SHOW GRANTS FOR test1localhost;2.分配权限 # 分配用户所有权限在for_end_test库的test1表 GRANT ALL PRIVILEGES ON for_end_t…

五分钟学完朴素贝叶斯算法

下面再描述一个详细的案例 个人感觉如下链接讲的比较详细 图解机器学习 | 朴素贝叶斯算法详解 - 知乎

2.3物理层下面的传输媒体

目录 2.3物理层下面的传输媒体2.3.1导引型传输媒体1.双绞线2.同轴电缆3.光纤 2.3.2非导引型传输媒体无线电微波通信 2.3物理层下面的传输媒体 传输媒体是数据传输系统中在发送器和接收器之间的物理通路 两大类: 导引型传输媒体:电磁波被导引沿着固体媒体…

新产品推广选品牌外包广州迅腾文化传播多渠道传播能力

在当今激烈的市场竞争中,新产品推广已成为企业发展的关键。选择具备多渠道传播能力的品牌外包服务提供商,有助于快速提升品牌知名度和市场占有率。作为行业领先者,迅腾文化凭借卓越的多渠道传播能力,成为企业新产品推广的理想合作…

国家开放大学形成性考核 统一考试 资料参考

试卷代号:11141 工程经济与管理 参考试题 一、单项选择题(每题2分,共20分) 1.资金的时间价值( )。 A.现在拥有的资金在将来投资时所能获得的利益 B.现在拥有的资金在将来消费时所付出的福利损失 C.…

saas 多租户系统数据隔离方案

关注WX公众号: commindtech77, 获得数据资产相关白皮书下载地址 1. 回复关键字:数据资源入表白皮书 下载 《2023数据资源入表白皮书》 2. 回复关键字:光大银行 下载 光大银行-《商业银行数据资产会计核算研究报告》 3. 回复关键字…

FreeRTOS学习第5篇--任务优先级

目录 FreeRTOS学习第5篇--任务优先级任务优先级设计实验任务一StartDefaultTask任务相关代码片段任务二ColorLED_Test任务相关代码片段任务三IRReceiver_Task相关代码片段实验现象本文中使用的测试工程 FreeRTOS学习第5篇–任务优先级 本文目标:学习与使用FreeRTOS…

关键字:throw关键字

在 Java 中,throw关键字用于抛出异常。当程序执行过程中发生意外情况,如错误的输入、资源不足、错误的逻辑等,导致程序无法正常执行下去时,可以使用throw关键字抛出异常。 以下是使用throw关键字的一些示例: 抛出异常…

C++程序编译

GCC编译器 文章目录 GCC编译器 源文件 为 Main.cpp 注意cpp文件 一定要用g命令 否则没办法执行 预处理(Pre-Processing):首先会经过预处理器将程序中的预编译指令进行处理,然后把源文件中的注释这些没用的东西都给扬了。 g -E Mai…

[线代]不挂科猴博士

行列式的性质 行列式的计算及应用 矩阵的运算上(加减,相乘,取行列式) 矩阵的运算下(转置,逆,秩) 向量组与线性空间 解方程组

打不开的U盘还能恢复吗?U盘打不开恢复方法

U盘打不开是用户在使用过程中经常遇到的问题,通常是由于驱动器问题、文件系统损坏、U盘硬件故障等原因引起的。本文将详细分析这些原因,并提供相应的解决方法,帮助用户快速解决U盘打不开的问题。 当U盘打不开时,想要保留其中的文件…

学习使用wps将ppt的页面保存为图片的方法

学习使用wps将ppt的页面保存为图片的方法 方案 方案 1、打开ppt,点击文件,另存为,选择文件类型为图片格式,jpg或者png,如下图: 2、点击每张幻灯片

Codeforces Round 900 (Div. 3)(A-F)

比赛链接 : Dashboard - Codeforces Round 900 (Div. 3) - Codeforces A. How Much Does Daytona Cost? 题面 : 思路 : 在序列中只要找到k&#xff0c;就返回true ; 代码 : #include<bits/stdc.h> #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)…

将PPT4页并排成1页

将PPT4页并排成1页打印 解决方法: 方法一 在打印时选择&#xff1a; 打开 PPT&#xff0c;点击文件选项点击打印点击整页幻灯片点击4张水平放置的幻灯平页面就会显示4张PPT显示在一张纸上 方法二 另存为PDF&#xff1a; 打开电脑上的目标PPT文件&#xff0c;点击文件点击…

数据结构期末复习(fengkao课堂)

学习数据结构时&#xff0c;以下建议可能对您有所帮助&#xff1a; 理解基本概念&#xff1a;首先&#xff0c;确保您理解数据结构的基本概念&#xff0c;例如数组、链表、栈、队列、树、图等。了解它们的定义、特点和基本操作。 学习时间复杂度和空间复杂度&#xff1a;了解如…

深度学习中Batch/Layer/Instance/Group normalization方法

图片中&#xff0c;N是batch size&#xff0c; c是channel。 BN&#xff1a;在每一个channel内&#xff0c;对H&#xff0c;W&#xff0c;Batch做平均LN&#xff1a;在每一个batch内&#xff0c;对H&#xff0c;W&#xff0c;Channel做平均IN&#xff1a;在每一个channel和bat…

数据结构:队列(链表和数组模拟实现)

目录 1.何为队列 2.链表模拟实现 2.1 节点和队列创建 2.2 初始化队列 2.3 入队操作 2.4 出队操作 2.5 遍历队列 2.6 获取队首和队尾元素 2.7 判断队列是否为空 2.8 完整实现 3. 数组模拟实现 3.1 创建队列 3.2 入队和出队操作 3.3 遍历队列 3.4 获取队首和队尾元…
最新文章