块设备驱动(1)-什么是块设备驱动?块设备驱动概念总结

news2024/9/28 13:23:40

1.块设备驱动概念

块设备驱动是针对存储设备,例如SD卡、EMMC、NAND FLASH、NOR FLSASH。
块设备驱动以块为单位进行访问、最小寻址单位是扇区、一个块中包含多个扇区、支持随机访问、带缓冲区,,当发生写入操作时,并不会立马操作硬盘,因为写之前要擦除,擦写频繁影响flash寿命。对于flash设备而言,顺序读写扇区能极大提升效率,所以对于不对存储设备,Linux里有实现不同的I/O调度算法。
image.png
而字符设备以字节为单位,不需要缓冲,只能被顺序读写

借鉴一张在网上找到的图片,框架图当然是学习中必不可备的东西,一眼便能有个清晰的认识
image.png
其中块设备驱动请求有两种形式,使用requeset_queue请求队列,以及不使用请求队列的。

2.常用结构体

2.1 block_device结构体

linux内核使用 block_device结构体表示块设备对象, 定义在include/linux/fs.h文件。

struct block_device {
	dev_t			bd_dev;  /* not a kdev_t - it's a search key */
	int			bd_openers;
	struct inode *		bd_inode;	/* will die */
	struct super_block *	bd_super;
	struct mutex		bd_mutex;	/* open/close mutex */
	struct list_head	bd_inodes;
	void *			bd_claiming;
	void *			bd_holder;
	int			bd_holders;
	bool			bd_write_holder;
#ifdef CONFIG_SYSFS
	struct list_head	bd_holder_disks;
#endif
	struct block_device *	bd_contains;
	unsigned		bd_block_size;
	struct hd_struct *	bd_part;
	/* number of times partitions within this device have been opened. */
	unsigned		bd_part_count;
	int			bd_invalidated;
	struct gendisk *	bd_disk;
	struct request_queue *  bd_queue;
	struct list_head	bd_list;
	/*
	 * Private data.  You must have bd_claim'ed the block_device
	 * to use this.  NOTE:  bd_claim allows an owner to claim
	 * the same device multiple times, the owner must take special
	 * care to not mess up bd_private for that case.
	 */
	unsigned long		bd_private;

	/* The counter of freeze processes */
	int			bd_fsfreeze_count;
	/* Mutex for freeze */
	struct mutex		bd_fsfreeze_mutex;
};

2.2 gendisk结构体

linux 内核使用 gendisk 来描述一个磁盘设备,定义include/linux/genhd.h

struct gendisk {
	/* major, first_minor and minors are input parameters only,
	 * don't use directly.  Use disk_devt() and disk_max_parts().
	 */
	int major;			/* major number of driver */
	int first_minor;
	int minors;                     /* maximum number of minors, =1 for
                                         * disks that can't be partitioned. */

	char disk_name[DISK_NAME_LEN];	/* name of major driver */
	char *(*devnode)(struct gendisk *gd, umode_t *mode);

	unsigned int events;		/* supported events */
	unsigned int async_events;	/* async events, subset of all */

	/* Array of pointers to partitions indexed by partno.
	 * Protected with matching bdev lock but stat and other
	 * non-critical accesses use RCU.  Always access through
	 * helpers.
	 */
	struct disk_part_tbl __rcu *part_tbl;
	struct hd_struct part0;

	const struct block_device_operations *fops;
	struct request_queue *queue;
	void *private_data;

	int flags;
	struct device *driverfs_dev;  // FIXME: remove
	struct kobject *slave_dir;

	struct timer_rand_state *random;
	atomic_t sync_io;		/* RAID */
	struct disk_events *ev;
#ifdef  CONFIG_BLK_DEV_INTEGRITY
	struct blk_integrity *integrity;
#endif
	int node_id;
};

major:主设备号
first_minor:第一个次设备号
minors:次设备号数量
part_tbl:磁盘对应的分区表
fops:块设备操作集
queue:磁盘对应的请求队列

2.3 block_device_operations 结构体

struct block_device_operations {
	int (*open) (struct block_device *, fmode_t);
	void (*release) (struct gendisk *, fmode_t);
	int (*rw_page)(struct block_device *, sector_t, struct page *, int rw);
	int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
	int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
	long (*direct_access)(struct block_device *, sector_t,
					void **, unsigned long *pfn, long size);
	unsigned int (*check_events) (struct gendisk *disk,
				      unsigned int clearing);
	/* ->media_changed() is DEPRECATED, use ->check_events() instead */
	int (*media_changed) (struct gendisk *);
	void (*unlock_native_capacity) (struct gendisk *);
	int (*revalidate_disk) (struct gendisk *);
	int (*getgeo)(struct block_device *, struct hd_geometry *);
	/* this callback is with swap_lock and sometimes page table lock held */
	void (*swap_slot_free_notify) (struct block_device *, unsigned long);
	struct module *owner;
};

open :打开指定的块设备
release:释放指定块设备
rw_page :数用于读写指定的页
ioctl:块设备IO控制
compat_ioctl:32位设备调用的ioctl函数
getgeo:获取磁盘信息,包括磁头、柱面和扇区
owner:标明属于哪个模块

3.块设备操作常用API

3.1块设备操作

1)注册块设备
int register_blkdev(unsigned int major, const char *name)
2)注销块设备
void unregister_blkdev(unsigned int major, const char *name)

major:主设备号
name:块设备名字

3.2gendisk相关操作

1)申请gendisk
struct gendisk *alloc_disk(int minors)
minors:次设备号数量、即gendisk对应的分区数量

2)删除 gendisk
void del_gendisk(struct gendisk *gp)

3)将 gendisk 添加到内核
void add_disk(struct gendisk *disk)
disk:要添加的gendisk结构体

4)设置 gendisk 容量
void set_capacity(struct gendisk *disk, sector_t size)
size:扇区数量大小,块设备驱动之间的扇区固定512字节,无论物理扇区是多大

5)增加gendisk 引用计数
struct kobject *get_disk(struct gendisk *disk)

5)减小gendisk 引用计数
void put_disk(struct gendisk *disk)

4.块设备IO请求过程

4.1 请求队列 request_queue

内核将块设备的读写,都放入到请求队列request_queue中,通过request请求,其中包含块设备从哪个地址读取,读取的数据长度,读取到哪里等。每个gendisk结构体中都有一个requeset_queue,即每个磁盘gendisk都会分配一个request_queue。
初始化请求队列之后,赋值给gendisk的queue成员变量
1)初始化请求队列(使用默认IO调度器)
当访问外部硬件、光盘、磁盘等,需要控制访问顺序来提升访问效率,则需要使用默认IO调度器,即需要使用这个API
request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)
rfn:请求处理函数指针
lcok:自旋锁指针

2)删除请求队列
void blk_cleanup_queue(struct request_queue *q)
q:删除的请求队列

3)分配请求队列(不使用默认IO调度器),并绑定制造请求函数
当访问简单的SD卡等,可以进行随机访问,不需要控制访问顺序来提升访问效率,则不需要设置IO调度器,即可直接使用这个API
struct request_queue *blk_alloc_queue(gfp_t gfp_mask)
gfp_mask : 内存分配的方式。 GFP_KERNEL和GFP_ATOMIC,
GFP_ATOMIC: 用来从中断处理和进程上下文之外的其他代码中分配内存. 从不睡眠
GFP_KERNEL: 内核内存的正常分配. 可能睡眠

void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn)
mfs:make_request_fn函数指针
blk_alloc_queue不使用默认IO调度器,需要自己实现mfs函数。上层代码向请求队列发生请求,调用make_request_fn函数。

4.2 请求request

请求队列(request_queue)里面包含的就是一系列的请求(request),其中的bio成员变量保存真正的数据。

1)获取请求
从request_queue中依次获取每个request
request *blk_peek_request(struct request_queue *q)
q:指定request_queue
返回值:request_queue 中下一个要处理的请求(request)

2)开启请求
从request_queue中获取到request后,调用该函数开始处理这个request
void blk_start_request(struct request *req)
**req:**要开始处理的请求

3)获取并处理请求
一次性完成请求的获取和开启
struct request *blk_fetch_request(struct request_queue *q)

4)其他API

blk_end_request()   //请求中指定字节数据被处理完成。 
blk_end_request_all()  //请求中所有数据全部处理完成。 
blk_end_request_cur()  //当前请求中的 chunk。 
blk_end_request_err()  //处理完请求,直到下一个错误产生。 
__blk_end_request()  //和 blk_end_request 函数一样,但是需要持有队列锁。 
__blk_end_request_all()  //和 blk_end_request_all 函数一样,但是需要持有队列锁。 
__blk_end_request_cur() //和 blk_end_request_cur 函数一样,但是需要持有队列锁。 
__blk_end_request_err() //和 blk_end_request_err 函数一样,但是需要持有队列锁

4.3 bio结构

每个 request 里面会有多个 bio,bio 保存着最终要读写的数据、地址等信息。
从 request_queue 中取出一个一个的 request,然后再从每个 request 里面取出 bio, 最后根据 bio 的描述讲数据写入到块设备,或者从块设备中读取数据
image.png

源码如下:

struct bio {
	struct bio		*bi_next;	/* request queue link */
	struct block_device	*bi_bdev;
	unsigned long		bi_flags;	/* status, command, etc */
	unsigned long		bi_rw;		/* bottom bits READ/WRITE,
						 * top bits priority
						 */
	struct bvec_iter	bi_iter;
	/* Number of segments in this BIO after
	 * physical address coalescing is performed.
	 */
	unsigned int		bi_phys_segments;
	/*
	 * To keep track of the max segment size, we account for the
	 * sizes of the first and last mergeable segments in this bio.
	 */
	unsigned int		bi_seg_front_size;
	unsigned int		bi_seg_back_size;

	atomic_t		bi_remaining;

	bio_end_io_t		*bi_end_io;

	void			*bi_private;
#ifdef CONFIG_BLK_CGROUP
	/*
	 * Optional ioc and css associated with this bio.  Put on bio
	 * release.  Read comment on top of bio_associate_current().
	 */
	struct io_context	*bi_ioc;
	struct cgroup_subsys_state *bi_css;
#endif
	union {
#if defined(CONFIG_BLK_DEV_INTEGRITY)
		struct bio_integrity_payload *bi_integrity; /* data integrity */
#endif
	};

	unsigned short		bi_vcnt;	/* how many bio_vec's */

	/*
	 * Everything starting with bi_max_vecs will be preserved by bio_reset()
	 */

	unsigned short		bi_max_vecs;	/* max bvl_vecs we can hold */

	atomic_t		bi_cnt;		/* pin count */

	struct bio_vec		*bi_io_vec;	/* the actual vec list */

	struct bio_set		*bi_pool;

	/*
	 * We can inline a number of vecs at the end of the bio, to avoid
	 * double allocations for a small number of bio_vecs. This member
	 * MUST obviously be kept at the very end of the bio.
	 */
	struct bio_vec		bi_inline_vecs[0];
};


struct bvec_iter {
    sector_t bi_sector; /* I/O 请求的设备起始扇区(512 字节) */
    unsigned int bi_size; /* 剩余的 I/O 数量 */
    unsigned int bi_idx; /* blv_vec 中当前索引 */
    unsigned int bi_bvec_done; /* 当前 bvec 中已经处理完成的字节数 */
};

struct bio_vec {
    struct page *bv_page; /* 页 */
    unsigned int bv_len; /* 长度 */	
    unsigned int bv_offset; /* 偏移 */
};

其中的bio与bio_iter 与 bio_vec之间的关系如下图所示,他们协同完成描述了数据源、数据长度以及数据目的地。
image.png

4.3.1 bio操作API

/// 遍历bio请求
#define __rq_for_each_bio(_bio, rq) \
     if ((rq->bio)) \
         for (_bio = (rq)->bio; _bio; _bio = _bio->bi_next)

/// 遍历bio中的段
#define bio_for_each_segment(bvl, bio, iter) \
     __bio_for_each_segment(bvl, bio, iter, (bio)->bi_iter)

/// 通知bio处理函数
/// 当不使用默认IO调度器,而使用blk_alloc_queue 自己实现制造请求
/// 则在bio处理完成后要通知内核bio处理完成
bvoid bio_endio(struct bio *bio, int error);

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

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

相关文章

P1948 [USACO08JAN] Telephone Lines S

Here 典中之典!! 解题思路 可选k条边代价为0如何决策? 将到当前位置选择了几条代价为0的边放入状态,即若当前状态选的边数小于,则可以进行决策,是否选择当前边,若选,则&#xff0c…

[2024-03-09 19:55:01] [42000][1067] Invalid default value for ‘create_time‘【报错】

这个错误可能是因为你的 MySQL 数据库版本不支持 CURRENT_TIMESTAMP 作为默认值。在一些早期版本中,MySQL 对 TIMESTAMP 类型字段的默认值设置有限制,只允许使用特定的常量值(如 0000-00-00 00:00:00 或 CURRENT_TIMESTAMP())。如…

选修-单片机作业第1/2次

第一次作业 第二次作业 1、51 系列单片机片内由哪几个部分组成?各个部件的最主要功能是什么? 51系列单片机的内部主要由以下几个部分组成,每个部件的主要功能如下: 1. **中央处理器(CPU)**:这是…

Cisco Packet Tracer 模拟器实现一些交换机的基本配置

1. 内容 应用Cisco Packet Tracer 5.3搭建网络 应用Cisco Packet Tracer 5.3配置网络 通过不同的命令实现交换机的基本配置,包括交换机的各种配置模式、交换机的基本配置、交换机的端口配置。 2. 过程 2.1 打开软件 安装模拟器后打开如下: 图1 安装并…

Linux 之九:CentOS 上 Tomcat 安装、SpringBoot 项目打包和部署

安装 Tomcat 下载 a. 方式一:可以在windows 真机上下载后,再上传到服务器 b. 方式二:可以在服务器端使用 wget 下载命令来下载 登录官网https://tomcat.apache.org/download-90.cgi,选择 linux 版本 右键,获取下载链接…

【EtherCAT实践篇】九、EtherCAT增加变量示例:增加浮点数输入变量

目的:在EtherCAT开发板上IO程序基础上进行修改,将原来的16位整数型数据Analog input改为32位浮点数,基于STM32F405底板。 1、XML配置修改 1.1 更改数据类型 ETG1020基础数据中包括浮点数 REAL,可以直接使用浮点数。 这里在xml…

STM32 | 库函数与寄存器开发区别及LED等和按键源码(第三天)

STM32 | STM32F407ZE(LED寄存器开发续第二天源码)STM32 第三天 一、 库函数与寄存器开发区别 1.1 寄存器 寄存器开发优点 直接操作寄存器,运行效率高。 寄存器开发缺点 1、开发难度大,开发周期长 2、代码可阅读性差,可移植差 3、后期维护难度高 1.2库函数 库函数开…

稀碎从零算法笔记Day14-LeetCode:同构字符串

题型:字符串、哈希表 链接:205. 同构字符串 - 力扣(LeetCode) 来源:LeetCode 题目描述 给定两个字符串 s 和 t ,判断它们是否是同构的。 如果 s 中的字符可以按某种映射关系替换得到 t ,那…

017集——圆弧(ARC)转多段线(lwpolyline)——cad vba 中按一定精度拟合加点实现

在国土资源管理项目中,我们经常会遇到CAD转gis数据实现入库,而cad中的arc圆弧转为gis数据只能转出弧的顶点坐标,导致图形变形失真。若一个一个对弧进行手工增加点转为多段线,耗时耗力,效率极其低下。这里给出解决方案&…

FL Studio21.2.3最新国内中文永久版本下载

当然,FL Studio 21作为一款先进的音乐制作软件,除了之前提到的核心功能外,还拥有许多独特的特色和优势,使其在竞争激烈的市场中脱颖而出。以下是对FL Studio 21的更多特色的详细介绍: FL Studio 21 Win-安装包下载如下…

Android Studio 检测不到 IDE 更新

点击 Configure updates...,不知道什么时候把 Check for IDE updates 去勾了,一年没有检测到更新! 只能重新安装,掉坑里了!

【JVM】聊聊垃圾回收之三色标记算法

在垃圾收集器 CMS中存在四个阶段,初始标记、并发标记、重新标记、并发清理。 那么在并发标记中由于没有STW,业务程序和GC程序是并发执行的,那么是如何实现对象的并发标记的。 并发垃圾回收 并发标记其实是一个宏观的过程,仍然需…

OpenGrok代码服务器搭建,解决代码检索慢的问题

一、背景 在前一家公司,公司提供了OpenGrok服务器供大家检索查阅代码。但在新公司,大家都使用vscode或Sourse Insight,这就存在一些问题: 不能跳转或者跳转比较慢。 搜索查询速度慢,且结果展示不易查看。 这严重影…

《领导的气场——8堂课讲透中国式领导智慧》读书笔记

整体感悟 个人感觉书籍比较偏说教、理论,没有看完。 现仅仅摘录自己“心有戚戚焉”的内容。 经典摘录 管理的本质是通过别人完成任务。有一百件事情,一个人都做了,那只能叫勤劳;有一百件事情,主事的人自己一件也不做&…

FreeRTOS学习笔记-基于stm32(3)中断管理

一、什么是中断 通俗点讲就是让CPU停止当前在做的事,转而去做更紧急的事。 二、中断优先级分组 这个紧急的事也有一个等级之分,优先级越高越先执行。stm32使用中断优先配置寄存器的高4位,共16级的中断优先等级。 stm32的中断优先等级可以分为…

基于河马优化算法(Hippopotamus optimization algorithm,HO)的无人机三维路径规划

一、无人机路径规划模型介绍 二、算法介绍 河马优化算法(Hippopotamus optimization algorithm,HO)由Amiri等人于2024年提出,该算法模拟了河马在河流或池塘中的位置更新、针对捕食者的防御策略以及规避方法。2024最新算法&#x…

在DeepLn环境中安装VLLM与ChatGLM3

DeepLn | 智慧算力触手可及是一个挺便宜的算力租用平台,里面有大量的显卡可以租用。唯一美中不足的是,提供的pytorch版本低,只支持到2.01,为了匹配vllm,需要手动安装指定版本的pytorch。 vllm介绍 总体而言&#xff0…

denied: requested access to the resource is denied报错解决

Docker 镜像在文件中包含一组指令,可在 Docker 容器中执行代码。大多数情况下,在创建 docker 镜像之后,当尝试将镜像推送到远程仓库时,会发生这种类型的报错“Docker denied: requested access to the resource is denied” 由于错…

FIT介绍-0

1、背景 FIT是flattened image tree的简称,它采用了device tree source file(DTS)的语法,生成的image文件也和dtb文件类似(称做itb)。 结构如下图: 其中image source file(.its)和device tree …

花店小程序有哪些功能 怎么制作

​花店小程序可以为花店提供一个全新的线上销售平台,帮助花店扩大市场份额,提升用户体验,增加销售额。下面我们来看看花店小程序应该具备哪些功能,以满足用户的需求。 1. 商品展示:展示花店的各类花卉和花束&#xff…