Linux 块设备驱动

Linux 三大驱动分别是:字符设备驱动、块设备驱动、网络设备驱动。

块设备是针对存储设备的,比如 SD 卡、EMMC、NAND Flash、Nor Flash、SPI Flash、机械硬盘、固态硬盘等。因此块设备驱动其实就是这些存储设备驱动,块设备驱动相比字符设备驱动的主要区别如下:

①、块设备只能以块为单位进行读写访问,块是 linux 虚拟文件系统(VFS)基本的数据传输
单位。字符设备是以字节为单位进行数据传输的,不需要缓冲。

②、块设备在结构上是可以进行随机访问的,对于这些设备的读写都是按块进行的,块设备使用缓冲区来暂时存放数据,等到条件成熟以后再一次性将缓冲区中的数据写入块设备中。
这么做的目的为了提高块设备寿命,大家如果仔细观察的话就会发现有些硬盘或者 NAND
Flash 就会标明擦除次数(flash 的特性,写之前要先擦除),比如擦除 100000 次等。因此,为了提高块设备寿命而引入了缓冲区,数据先写入到缓冲区中,等满足一定条件后再一次性写入到真正的物理存储设备中,这样就减少了对块设备的擦除次数,提高了块设备寿命。

字符设备是顺序的数据流设备,字符设备是按照字节进行读写访问的。字符设备不需要缓
冲区,对于字符设备的访问都是实时的,而且也不需要按照固定的块大小进行访问。

块设备结构的不同其 I/O 算法也会不同,比如对于 EMMC、SD 卡、NAND Flash 这类没
有任何机械设备的存储设备就可以任意读写任何的扇区(块设备物理存储单元)。但是对于机械硬盘这样带有磁头的设备,读取不同的盘面或者磁道里面的数据,磁头都需要进行移动,因此对于机械硬盘而言,将那些杂乱的访问按照一定的顺序进行排列可以有效提高磁盘性能,linux 里面针对不同的存储设备实现了不同的 I/O 调度算法。

块设备驱动框架

Linux 内核使用 block_device 结构体表示块设备

24  struct block_device {
25  	sector_t		bd_start_sect;
26  	struct disk_stats __percpu *bd_stats;
27  	unsigned long		bd_stamp;
28  	bool			bd_read_only;	/* read-only policy */
29  	dev_t			bd_dev;
30  	int			bd_openers;
31  	struct inode *		bd_inode;	/* will die */
32  	struct super_block *	bd_super;
33  	void *			bd_claiming;
34  	struct device		bd_device;
35  	void *			bd_holder;
36  	int			bd_holders;
37  	bool			bd_write_holder;
38  	struct kobject		*bd_holder_dir;
39  	u8			bd_partno;
40  	spinlock_t		bd_size_lock; /* for bd_inode->i_size updates */
41  	struct gendisk *	bd_disk;
42  
43  	/* The counter of freeze processes */
44  	int			bd_fsfreeze_count;
45  	/* Mutex for freeze */
46  	struct mutex		bd_fsfreeze_mutex;
47  	struct super_block	*bd_fsfreeze_sb;
48  
49  	struct partition_meta_info *bd_meta_info;
50  #ifdef CONFIG_FAIL_MAKE_REQUEST
51  	bool			bd_make_it_fail;
52  #endif
53  
54  	ANDROID_KABI_RESERVE(1);
55  	ANDROID_KABI_RESERVE(2);
56  	ANDROID_KABI_RESERVE(3);
57  	ANDROID_KABI_RESERVE(4);
58  } __randomize_layout;

对于 block_device 结构体,我们重点关注一下第 41 行的 bd_disk 成员变量,此成员变量为 gendisk 结构体指针类型,内核使用 block_device 来表示一个具体的块设备对象,比如一个硬盘或者分区,如果是硬盘的话,bd_disk 就指向通用磁盘结构 gendisk。

注册块设备:和字符设备驱动一样,我们需要向内核注册新的块设备、申请设备号,块设备注册函数为 register_blkdev

注销块设备:和字符设备驱动一样,如果不使用某个块设备了,那么就需要注销掉,函数为
unregister_blkdev

Linux 内核使用 gendisk 结构体来描述一个磁盘设备

121  struct gendisk {
122  	/* major, first_minor and minors are input parameters only,
123  	 * don't use directly.  Use disk_devt() and disk_max_parts().
124  	 */
125  	int major;			/* major number of driver */
126  	int first_minor;
127  	int minors;                     /* maximum number of minors, =1 for
128                                           * disks that can't be partitioned. */
129  
130  	char disk_name[DISK_NAME_LEN];	/* name of major driver */
131  
132  	unsigned short events;		/* supported events */
133  	unsigned short event_flags;	/* flags related to event processing */
134  
135  	struct xarray part_tbl;
136  	struct block_device *part0;
137  
138  	const struct block_device_operations *fops;
139  	struct request_queue *queue;
140  	void *private_data;
141  
142  	int flags;
143  	unsigned long state;
144  #define GD_NEED_PART_SCAN		0
145  #define GD_READ_ONLY			1
146  #define GD_DEAD				2
147  
148  	struct mutex open_mutex;	/* open/close mutex */
149  	unsigned open_partitions;	/* number of open partitions */
150  
151  	struct backing_dev_info	*bdi;
152  	struct kobject *slave_dir;
153  #ifdef CONFIG_BLOCK_HOLDER_DEPRECATED
154  	struct list_head slave_bdevs;
155  #endif
156  	struct timer_rand_state *random;
157  	atomic_t sync_io;		/* RAID */
158  	struct disk_events *ev;
159  #ifdef  CONFIG_BLK_DEV_INTEGRITY
160  	struct kobject integrity_kobj;
161  #endif	/* CONFIG_BLK_DEV_INTEGRITY */
162  #if IS_ENABLED(CONFIG_CDROM)
163  	struct cdrom_device_info *cdi;
164  #endif
165  	int node_id;
166  	struct badblocks *bb;
167  	struct lockdep_map lockdep_map;
168  	u64 diskseq;
169  
170  	ANDROID_KABI_RESERVE(1);
171  	ANDROID_KABI_RESERVE(2);
172  	ANDROID_KABI_RESERVE(3);
173  	ANDROID_KABI_RESERVE(4);
174  };

major 为磁盘设备的主设备号。first_minor 为磁盘的第一个次设备号。minors 为磁盘的此设备号数量,也就是磁盘的分区数量,这些分区的主设备号一样,此设备号不同。fops 为块设备操作集,为 block_device_operations 结构体类型。和字符设备操作集 file_operations 一样,是块设备驱动中的重点!queue 为磁盘对应的请求队列,所以针对该磁盘设备的请求都放到此队列中,驱动程序需要处理此队列中的所有请求。

编写块的设备驱动的时候需要分配并初始化一个 gendisk,linux 内核提供了一组 gendisk 操作函数:

申请 gendisk:
struct gendisk *alloc_disk(int minors)删除 gendisk:
void del_gendisk(struct gendisk *gp)将 gendisk 添加到内核:
void add_disk(struct gendisk *disk)设置 gendisk 容量:
void set_capacity(struct gendisk *disk, sector_t size)调整 gendisk 引用计数:
truct kobject * get_disk_and_module (struct gendisk *disk)
void put_disk(struct gendisk *disk)

和字符设备的 file_operations 一样,块设备也有操作集,为结构体 block_device_operations

1935  struct block_device_operations {
1936  	blk_qc_t (*submit_bio) (struct bio *bio);
1937  	int (*open) (struct block_device *, fmode_t);
1938  	void (*release) (struct gendisk *, fmode_t);
1939  	int (*rw_page)(struct block_device *, sector_t, struct page *, unsigned int);
1940  	int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
1941  	int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
1942  	unsigned int (*check_events) (struct gendisk *disk,
1943  				      unsigned int clearing);
1944  	void (*unlock_native_capacity) (struct gendisk *);
1945  	int (*getgeo)(struct block_device *, struct hd_geometry *);
1946  	int (*set_read_only)(struct block_device *bdev, bool ro);
1947  	/* this callback is with swap_lock and sometimes page table lock held */
1948  	void (*swap_slot_free_notify) (struct block_device *, unsigned long);
1949  	int (*report_zones)(struct gendisk *, sector_t sector,
1950  			unsigned int nr_zones, report_zones_cb cb, void *data);
1951  	char *(*devnode)(struct gendisk *disk, umode_t *mode);
1952  	struct module *owner;
1953  	const struct pr_ops *pr_ops;
1954  
1955  	/*
1956  	 * Special callback for probing GPT entry at a given sector.
1957  	 * Needed by Android devices, used by GPT scanner and MMC blk
1958  	 * driver.
1959  	 */
1960  	int (*alternative_gpt_sector)(struct gendisk *disk, sector_t *sector);
1961  
1962  	ANDROID_KABI_RESERVE(1);
1963  	ANDROID_KABI_RESERVE(2);
1964  	ANDROID_OEM_DATA(1);
1965  };

open 函数用于打开指定的设备。release 函数用于关闭(释放)指定的块设备。rw_page 函数用于读写指定的页。ioctl 函数用于块设备 I/O 控制。compat_ioctl 函数与 ioctl 函数一样,都是用于块设备的 I/O 控制。区别在于在 64位系统上,32 位应用程序的 ioctl 会调用 compat_iotl 函数。在 32 位系统上运行的 32 位应用程序调用的就是 ioctl 函数。getgeo 函数用于获取磁盘信息,包括磁头、柱面和扇区等信息。owner 表示此结构体属于哪个模块,一般直接设置为 THIS_MODULE。

块设备 I/O 请求过程

大家如果仔细观察的话会在 block_device_operations 结构体中并没有找到 read 和 write 这样的读写函数,那么块设备是怎么从物理块设备中读写数据?这里就引处理块设备驱动中非常重要的 request_queue、request 和 bio。

内核将对块设备的读写都发送到请求队列 request_queue 中,request_queue 中是大量的 request(请求结构体),而 request 又包含了 bio,bio 保存了读写相关数据,比如从块设备的哪个地址开始读取、读取的数据长度,读取到哪里,如果是写的话还包括要写入的数据等。

gendisk 结构体里面有一个 request_queue 结构体指针类型成员变量 queue,在编写块设备驱动的时候,每个磁盘(gendisk)都要分配一个 request_queue。

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

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

相关文章

jetson nano——编译一些包的网址导航,pyside2,qt(持续更新)

目录 1.PySide2下载地址2.tesserocr下载地址3.Qt下载地址4.OpenSSL官网5.latex编译器下载地址5.1MikTex5.2TeX Live 1.PySide2下载地址 https://download.qt.io/official_releases/QtForPython/pyside2/ 如下图: 2.tesserocr下载地址 https://github.com/simonflue…

ToolPlatform烧录HI3403实战

既然是嵌入式,烧录是逃不掉的。 连接串口!必须 主机有串口,或者用USB转接。 软件 01.software\pc\ToolPlatform 启动 其实只有这一个选项 BurnTool面板: 选择配置 选择烧写eMMC,再点击游览,选择xml…

NetSuite多脚本性能研究

在项目中,随着复杂度的提升,客制脚本以及各类SuiteAPP的应用,导致某个对象上挂载的脚本大量增加,最终导致了性能问题。表现在保存单据时时间过长,严重影响人机界面的用户感受。基于此问题,我们开展了NetSui…

大语言模型RAG-langchain models (二)

大语言模型RAG-langchain models (二) 往期文章:大语言模型RAG-技术概览 (一) 文章目录 大语言模型RAG-langchain models (二)**往期文章:[大语言模型RAG-技术概览 (一)](https://blog.csdn.net/tangbiubiu/article/details/136651625)**核心模块总览Mod…

《硬件历险》之Mac抢救出现问题的时间机器硬盘中的数据

本文虽然使用“抢救”一词,但是运气比较好,远没有达到访问和修改底层的信息来抢救的地步。如果你是需要通过访问和修改底层信息来抢救数据,建议阅读刘伟的《数据恢复技术深度揭秘(第二版)》或者寻找专业人士的帮助。 《…

关于 NXP PCA85073A 实时时钟读取数据时出现 IIC 传输失败的原因解析和解决方法

一、前言 对使用 I2C 传输的 RTC 外设 PCA85073,在 I2C 传输过程中若有复位信号输入,则有概率出现 I2C 死锁的状态,即 SCL为高,SDA一直为低的现象。 二、I2C 基本协议 在分析问题出现的原因之前,我…

es索引操作命令

索引操作 index 创建索引 put 方法创建索引 使用 put 创建索引时必须指明文档id,否则报错 # PUT 创建命令 # test1 索引名称 # type1 类型名称,默认为_doc,已经被废弃 # 1 文档id PUT /test1/type1/1 {"name":"zhangsan&…

【体验有奖】用 AI 画春天,函数计算搭建 Stable Diffusion WebUI

人工智能生成内容 AIGC(Artificial Intelligence Generated Content)是当下备受关注的概念之一,是继 PGC 和 UGC 之后的新型生产方式。AIGC 技术的核心思想是利用人工智能算法生成具有一定创意和质量的内容。例如,根据用户的描述或…

YOLOv9详解

1.概述 在逐层进行特征提取和空间转换的过程中,会损失大量信息,例如图中的马在建模过程中逐渐变得模糊,从而影响到最终的性能。YOLOv9尝试使用可编程梯度信息PGI解决这一问题。 具体来说, PGI包含三个部分,&#xff0…

AJAX 02 案例、Bootstrap框架

AJAX 学习 AJAX 2 综合案例黑马 API01 图书管理Bootstrap 官网Bootstrap 弹框图书管理-渲染列表图书管理-添加图书图书管理-删除图书图书管理 - 编辑图书 02 图片上传03 更换图片04 个人信息设置信息渲染头像修改补充知识点:label扩大表单的范围 AJAX 2 综合案例 黑…

【鸿蒙HarmonyOS开发笔记】自定义组件详解

自定义组件 除去系统预置的组件外,ArkTS 还支持自定义组件。使用自定义组件,可使代码的结构更加清晰,并且能提高代码的复用性。 我们开发的每个页面其实都可以视为自定义组件内置组件的结合 语法说明 自定义组件的语法如下图所示 各部分…

深度序列模型与自然语言处理:基于TensorFlow2实践

目录 写在前面 推荐图书 编辑推荐 内容简介 作者简介 推荐理由 写在最后 写在前面 本期博主给大家推荐一本深度学习的好书,对Python深度学习感兴趣的小伙伴快来看看吧! 推荐图书 《深度序列模型与自然语言处理 基于TensorFlow2实践》 直达链接…

基于centos7的k8s最新版v1.29.2安装教程

k8s概述 Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快速增长的生态,其服务、支持和工具的使用范围相当广泛。 Kubernetes 这个名字源于希腊语&…

AI系统性学习01- Prompt Engineering

文章目录 面向开发者的Prompt Engineering一、简介二、Prompt设计原则1 环境配置2.两个基本原则2.1 原则1:编写清晰、具体的指令2.1.1 策略一:分割2.1.2 策略2:结构化输出2.1.3 策略3:模型检测2.1.4 策略4:提供示例 2.…

css设置选中文字和选中图片字的颜色

要改变页面中选中文字的颜色,可以使用 CSS 的 ::selection 伪元素来实现 *::selection {/* 改变选中文字的背景色 */background-color: #c42121;/* 改变选中文字的文本颜色 */color: #fff; } 用通配符选择器给所有元素都加上了 ::selection伪元素,用于…

鸿蒙开发之MPChart图表开发

一、简介 随着移动应用的不断发展,数据可视化成为提高用户体验和数据交流的重要手段之一,因此需要经常使用图表,如折线图、柱形图等。OpenHarmony提供了一个强大而灵活的图表库是实现这一目标的关键。 在 ohpm 中心仓(https://ohpm.openharmony.cn/)中,汇聚了众多开发者…

Vue2 + node.js项目

1、Vue2 vue2主要功能包括登入、退出、用户权限、表格的增删改查、文件下载。 Vue2项目地址https://gitee.com/www6/finance1.git 2、node.js编写后端接口 2.1、项目初始化 后端地址https://gitee.com/www6/finance-backend.git 创建项目 npm install -g koa-generator …

基于JavaWeb+SSM+Vue“鼻护灵”微信小程序系统的设计和实现

基于JavaWebSSMVue“鼻护灵”微信小程序系统的设计和实现 滑到文末获取源码Lun文目录前言主要技术系统设计功能截图 滑到文末获取源码 Lun文目录 摘 要 3 Abstract 1 1 绪 论 1 1.1研究背景 1 工作的效率。 1 1.2 研究意义 1 1.3研究现状 1 1.4本文组织结构 2 2 技术介绍 3 2…

Flutter:构建美观应用的跨平台方案

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

Java数据结构二叉树练习

1.检查两棵二叉树是否都是相同的练习 我要求时间复杂度为1,所以我们不用前序中序后序是否都一样来进行判断 如何判断二叉树是否都是相同的子问题方式 先判断根节点是否相同 再判断左子树和右子树是否都是相同的 先用代码判断不相同的情况,都相同的化…