linux块设备驱动---相关结构体(转)
上回最后面介绍了相关数据结构,下面再详细介绍
块设备对象结构 block_device
内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区。如果该结构代表一个分区,则其成员bd_part指向设备的分区结构。如果该结构代表设备,则其成员bd_disk指向设备的通用硬盘结构gendisk
当用户打开块设备文件时,内核创建结构block_device实例,设备驱动程序还将创建结构gendisk实例,分配请求队列并注册结构block_device实例。
块设备对象结构block_device列出如下(在include/linux/fs.h中)
1 struct block_device {
2 dev_t bd_dev; /* not a kdev_t - it's a search key */
3 struct inode * bd_inode; /* 分区节点 */
4 struct super_block * bd_super;
5 int bd_openers;
6 struct mutex bd_mutex;/* open/close mutex 打开与关闭的互斥量*/
7 struct semaphore bd_mount_sem; /*挂载操作信号量*/
8 struct list_head bd_inodes;
9 void * bd_holder;
10 int bd_holders;
11 #ifdef CONFIG_SYSFS
12 struct list_head bd_holder_list;
13 #endif
14 struct block_device * bd_contains;
15 unsigned bd_block_size; /*分区块大小*/
16 struct hd_struct * bd_part;
17 unsigned bd_part_count; /*打开次数*/
18 int bd_invalidated;
19 struct gendisk * bd_disk; /*设备为硬盘时,指向通用硬盘结构*/
20 struct list_head bd_list;
21 struct backing_dev_info *bd_inode_backing_dev_info;
22 unsigned long bd_private;
23 /* The counter of freeze processes */
24 int bd_fsfreeze_count;
25 /* Mutex for freeze */
26 struct mutex bd_fsfreeze_mutex;
27 };
通用硬盘结构 gendisk
结构体gendisk代表了一个通用硬盘(generic hard disk)对象,它存储了一个硬盘的信息,包括请求队列、分区链表和块设备操作函数集等。块设备驱动程序分配结构gendisk实例,装载分区表,分配请求队列并填充结构的其他域。
支持分区的块驱动程序必须包含 <linux/genhd.h> 头文件,并声明一个结构gendisk,内核还维护该结构实例的一个全局链表gendisk_head,通过函数add_gendisk、del_gendisk和get_gendisk维护该链表。
结构gendisk列出如下(在include/linux/genhd.h中):
1 gendisk {
2 int major; /* 驱动程序的主设备号 */
3 int first_minor; /*第一个次设备号*/
4 int minors; /*次设备号的最大数量,没有分区的设备,此值为1 */
5 char disk_name[32]; /* 主设备号驱动程序的名字*/
6 struct hd_struct **part; /* 分区列表,由次设备号排序 */
7 struct block_device_operations *fops; /*块设备操作函数集*/
8 struct request_queue *queue; /*请求队列*/
9 struct blk_scsi_cmd_filter cmd_filter;
10 void *private_data; /*私有数据*/
11 sector_t capacity; /* 函数set_capacity设置的容量,以扇区为单位*/
12 int flags; /*设置驱动器状态的标志,如:可移动介质为
13 GENHD_FL_REMOVABLE*/
14 struct device dev; /*从设备驱动模型基类结构device继承*/
15 struct kobject *holder_dir;
16 struct kobject *slave_dir;
17 struct timer_rand_state *random;
18 int policy;
19 atomic_t sync_io; /* RAID */
20 unsigned long stamp;
21 int in_flight;
22 #ifdef CONFIG_SMP
23 struct disk_stats *dkstats;
24 #else
25 /*硬盘统计信息,如:读或写的扇区数、融合的扇区数、在请求队列的时间等*/
26 struct disk_stats dkstats;
27 #endif
28 struct work_struct async_notify;
29 #ifdef CONFIG_BLK_DEV_INTEGRITY
30 struct blk_integrity *integrity; /*用于数据完整性扩展*/
31 #endif
32 };
Linux内核提供了一组函数来操作gendisk,主要包括:
分配gendisk
struct gendisk *alloc_disk(int minors);
minors 参数是这个磁盘使用的次设备号的数量,一般也就是磁盘分区的数量,此后minors不能被修改。
增加gendisk
gendisk结构体被分配之后,系统还不能使用这个磁盘,需要调用如下函数来注册这个磁盘设备:
void add_disk(struct gendisk *gd);
特别要注意的是对add_disk()的调用必须发生在驱动程序的初始化工作完成并能响应磁盘的请求之后。
释放gendisk
当不再需要一个磁盘时,应当使用如下函数释放gendisk:
void del_gendisk(struct gendisk *gd);
设置gendisk容量
void set_capacity(struct gendisk *disk, sector_t size);
块设备中最小的可寻址单元是扇区,扇区大小一般是2的整数倍,最常见的大小是512字节。扇区的大小是设备的物理属性,扇区是所有块设备的基本单元,块设备 无法对比它还小的单元进行寻址和操作,不过许多块设备能够一次就传输多个扇区。虽然大多数块设备的扇区大小都是512字节,不过其它大小的扇区也很常见, 比如,很多CD-ROM盘的扇区都是2K大小。不管物理设备的真实扇区大小是多少,内核与块设备驱动交互的扇区都以512字节为单位。因此,set_capacity()函数也以512字节为单位。
分区结构hd_struct代表了一个分区对象,它存储了一个硬盘的一个分区的信息,驱动程序初始化时,从硬盘的分区表中提取分区信息,存放在分区结构实例中。
块设备操作函数集结构 block_device_operations
字符设备通过 file_operations 操作结构使它们的操作对系统可用. 一个类似的结构用在块设备上是 struct block_device_operations,
定义在 <linux/fs.h>.
int (*open)(struct inode *inode, struct file *filp);
int (*release)(struct inode *inode, struct file *filp);
就像它们的字符驱动对等体一样工作的函数; 无论何时设备被打开和关闭都调用它们. 一个字符驱动可能通过启动设备或者锁住门(为可移出的介质)来响应一个 open 调用. 如果你将介质锁入设备, 你当然应当在 release 方法中解锁.
int (*ioctl)(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg);
实现 ioctl 系统调用的方法. 但是, 块层首先解释大量的标准请求; 因此大部分的块驱动 ioctl 方法相当短.
PS:在block_device_operations中没有实际读或写数据的函数. 在块 I/O 子系统, 这些操作由请求函数处理
请求结构request
结构request代表了挂起的I/O请求,每个请求用一个结构request实例描述,存放在请求队列链表中,由电梯算法进行排序,每个请求包含1个或多个结构bio实例
1 request {
2 //用于挂在请求队列链表的节点,使用函数blkdev_dequeue_request访问它,而不能直接访
3 问
4 struct list_head queuelist;
5 struct list_head donelist; /*用于挂在已完成请求链表的节点*/
6 struct request_queue *q; /*指向请求队列*/
7 unsigned int cmd_flags; /*命令标识*/
8 enum rq_cmd_type_bits cmd_type; /*命令类型*/
9 /*各种各样的扇区计数*/
10 /*为提交i/o维护bio横断面的状态信息,hard_*成员是块层内部使用的,驱动程序不应该改变
11 它们*/
12 sector_t sector; /*将提交的下一个扇区*/
13 sector_t hard_sector; /* 将完成的下一个扇区*/
14 unsigned long nr_sectors; /* 整个请求还需要传送的扇区数*/
15 unsigned long hard_nr_sectors; /* 将完成的扇区数*/
16 /*在当前bio中还需要传送的扇区数 */
17 unsigned int current_nr_sectors;
18 /*在当前段中将完成的扇区数*/
19 unsigned int hard_cur_sectors;
20 struct bio *bio; /*请求中第一个未完成操作的bio*、
21 struct bio *biotail; /*请求链表中末尾的bio*、
22 struct hlist_node hash; /*融合 hash */
23 /* rb_node仅用在I/O调度器中,当请求被移到分发队列中时,
24 请求将被删除。因此,让completion_data与rb_node分享空间*/
25 union {
26 struct rb_node rb_node; /* 排序/查找*/
27 void *completion_data;
28 };
request结构体的主要成员包括:
sector_t hard_sector;
unsigned long hard_nr_sectors;
unsigned int hard_cur_sectors;
上述3个成员标识还未完成的扇区,hard_sector是第1个尚未传输的扇区,hard_nr_sectors是尚待完成的扇区数,hard_cur_sectors是并且当前I/O操作中待完成的扇区数。这些成员只用于内核块设备层,驱动不应当使用它们。
sector_t sector;
unsigned long nr_sectors;
unsigned int current_nr_sectors;
驱动中会经常与这3个成员打交道,这3个成员在内核和驱动交互中发挥着重大作用。它们以512字节大小为1个扇区,如果硬件的扇区大小不是512字节,则需要进行相应的调整。例如,如果硬件的扇区大小是2048字节,则在进行硬件操作之前,需要用4来除起始扇区号。
hard_sector、hard_nr_sectors、hard_cur_sectors与sector、nr_sectors、current_nr_sectors之间可认为是“副本”关系。
struct bio *bio;
bio是这个请求中包含的bio结构体的链表,驱动中不宜直接存取这个成员,而应该使用后文将介绍的rq_for_each_bio()。
请求队列结构request_queue
每个块设备都有一个请求队列,每个请求队列单独执行I/O调度,请求队列是由请求结构实例链接成的双向链表,链表以及整个队列的信息用结构request_queue描述,称为请求队列对象结构或请求队列结构。它存放了关于挂起请求的信息以及管理请求队列(如:电梯算法)所需要的信息。结构成员request_fn是来自设备驱动程序的请求处理函数。
请求队列结构request_queue列出如下(在/include/linux/blk_dev.h中)
太长了,此处略,其实也看不懂,- -#
Bio结构
通常1个bio对应1个I/O请求,IO调度算法可将连续的bio合并成1个请求。所以,1个请求可以包含多个bio。
内核中块I/O操作的基本容器由bio结构体表示,定义 在<linux/bio.h>中,该结构体代表了正在现场的(活动的)以片段(segment)链表形式组织的块I/O操作。一个片段是一小 块连续的内存缓冲区。这样的好处就是不需要保证单个缓冲区一定要连续。所以通过片段来描述缓冲区,即使一个缓冲区分散在内存的多个位置上,bio结构体也 能对内核保证I/O操作的执行,这样的就叫做聚散I/O.
bio为通用层的主要数据结构,既描述了磁盘的位置,又描述了内存的位置,是上层内核vfs与下层驱动的连接纽带
1 bio {
2 sector_t bi_sector;//该bio结构所要传输的第一个(512字节)扇区:磁盘的位置
3 struct bio *bi_next; //请求链表
4 struct block_device *bi_bdev;//相关的块设备
5 unsigned long bi_flags//状态和命令标志
6 unsigned long bi_rw; //读写
7 unsigned short bi_vcnt;//bio_vesc偏移的个数
8 unsigned short bi_idx; //bi_io_vec的当前索引
9 unsigned short bi_phys_segments;//结合后的片段数目
10 unsigned short bi_hw_segments;//重映射后的片段数目
11 unsigned int bi_size; //I/O计数
12 unsigned int bi_hw_front_size;//第一个可合并的段大小;
13 unsigned int bi_hw_back_size;//最后一个可合并的段大小
14 unsigned int bi_max_vecs; //bio_vecs数目上限
15 struct bio_vec *bi_io_vec; //bio_vec链表:内存的位置
16 bio_end_io_t *bi_end_io;//I/O完成方法
17 atomic_t bi_cnt; //使用计数
18 void *bi_private; //拥有者的私有方法
19 bio_destructor_t *bi_destructor; //销毁方法
20 };
内存数据段结构bio_vec
结构bio_vec代表了内存中的一个数据段,数据段用页、偏移和长度描
述。I/O需要执行的内存位置用段表示,结构bio指向了一个段的数组。
结构bio_vec列出如下(在include/linux/bio.h中):
struct bio_vec {
struct page *bv_page; /*数据段所在的页*/
unsigned short bv_len; /*数据段的长度*/
unsigned short bv_offset; /*数据段页内偏移*/
};
块设备各个结构体间关系
linux块设备驱动---相关结构体(转)的更多相关文章
- Linux 块设备驱动 (一)
1.块设备的I/O操作特点 字符设备与块设备的区别: 块设备只能以块为单位接受输入和返回输出,而字符设备则以字符为单位. 块设备对于I/O请求有对应的缓冲区,因此它们可以选择以什么顺序进行响应,字符设 ...
- linux块设备驱动
块设备驱动程序<1>.块设备和字符设备的区别1.读取数据的单元不同,块设备读写数据的基本单元是块,字符设备的基本单元是字节.2.块设备可以随机访问,字符设备只能顺序访问. 块设备的访问:当 ...
- linux块设备驱动之实例
1.注册:向内核注册个块设备驱动,其实就是用主设备号告诉内核这个代表块设备驱动 sbull_major = register_blkdev(sbull_major, "sbull&quo ...
- Linux块设备驱动详解
<机械硬盘> a:磁盘结构 -----传统的机械硬盘一般为3.5英寸硬盘,并由多个圆形蝶片组成,每个蝶片拥有独立的机械臂和磁头,每个堞片的圆形平面被划分了不同的同心圆,每一个同心圆称为一个 ...
- linux 块设备驱动 (三)块设备驱动开发
一: 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigne ...
- linux块设备驱动---程序设计(转)
块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigned i ...
- Linux块设备驱动(一) _驱动模型
块设备是Linux三大设备之一,其驱动模型主要针对磁盘,Flash等存储类设备,本文以3.14为蓝本,探讨内核中的块设备驱动模型 框架 下图是Linux中的块设备模型示意图,应用层程序有两种方式访问一 ...
- Linux块设备驱动_WDS
推荐书:<Linux内核源代码情景分析> 1.字符设备驱动和使用中等待某一事件的方法①查询方式②休眠唤醒,但是这种没有超时时间③poll机制,在休眠唤醒基础上加一个超时时间④异步通知,异步 ...
- linux块设备驱动(一)——块设备概念介绍
本文来源于: 1. http://blog.csdn.net/jianchi88/article/details/7212370 2. http://blog.chinaunix.net/uid-27 ...
随机推荐
- log4j日志文件输出保存
og4j.appender.A1=org.apache.log4j.DailyRollingFileAppender log4j.appender.A1.File=app.log log4j.appe ...
- pytest allure 生成html测试报告
前提:需要 java 1.8 以上.python3环境 一.下载pytest pip install pytest 二.下载Allure Pytest Adaptor插件 pip install py ...
- 手把手带你体验鸿蒙 harmonyOS
wNlRGd.png 前言 本文已经收录到我的 Github 个人博客,欢迎大佬们光临寒舍: 我的 GIthub 博客 学习导图 image.png 一.为什么要尝鲜 harmonyos? wNlfx ...
- git仓库个人和企业版新增仓库和成员
1.首先要在本地安装一个git安装包,比如安装完之后路径是在 D:\Program Files (x86)\Git\bin\git.exe 2.访问git网址 https://gitee. ...
- 按正常步骤对github的仓库进行push自己本地的代码提示push rejected
按正常步骤对github的仓库进行push自己本地的代码提示push rejected. 大概原因是:初始化项目时,远程仓库我建了README.md文件,而本地仓库与远程仓库尚未进行文件关联,因此需要 ...
- JavaCV与OpenCV的区别和使用中遇到的问题
写这篇随笔的原因是因为我用了JavaCV一段时间后项目情况糟透了,可能大家很熟悉OpenCV,也有一部分人熟悉JavaCV,但是我相信真正把JavaCV用到生产上的不是太多. 我参与图片处理项目快一个 ...
- 部署cobbler服务器
部署cobbler服务器 1.准备环境使用nat或者仅主机模式,不要使用桥接模式,方式获取的IP不是自己的 2. 配置yum源[epel]name=epelenabled=1gpgcheck=0bas ...
- netty全局分析1
这个系列都是别人的分析文 https://www.jianshu.com/p/ac7fb5c2640f 一丶 Netty基础入门 Netty是一个高性能.异步事件驱动的NIO框架,它提供了对TCP.U ...
- 10月1日之后,你新建的GitHub库默认分支不叫「master」了
从 2020 年 10 月 1 日开始,GitHub 上的所有新库都将用中性词「main」命名,取代原来的「master」,因为后者是一个容易让人联想到奴隶制的术语. 这个决定并不是最近才做出的.今年 ...
- Java基础一篇过(六)Java8--lambda表达式
一.简介 lambda表达式是Java8的一个重要特性,也可以称为闭包,常用于配合Java8的Stream对集合元素进行操作,使得代码更简介紧凑. 二.代码解析 虽说lambda表达式是一个新的特性, ...