Linux内核学习轨迹第七部:块设备的抽象与核心数据结构(第二节)
·
2. 块设备的抽象与核心数据结构
块设备子系统的核心是面向对象的抽象设计,把不同厂商、不同类型的块设备,抽象为统一的内核对象,向上提供无差别的访问接口。本章节基于Linux 6.6内核源码(定义在include/linux/blkdev.h/include/linux/genhd.h),完整拆解核心数据结构。
2.1 顶层磁盘抽象:struct gendisk
struct gendisk(通用磁盘结构体)是Linux内核对一个块设备的最高级抽象,代表一个独立的磁盘设备(比如/dev/sda、/dev/nvme0n1),无论是物理磁盘、分区、逻辑卷、RAID阵列,在内核中都对应一个gendisk实例。
每个块设备在内核中有且仅有一个gendisk实例,它包含了块设备的所有核心信息:设备名称、设备号、容量、IO队列、操作函数集、分区信息、驱动私有数据。
2.1.1 核心字段拆解
struct gendisk {
// 磁盘设备名称,对应/dev下的设备节点名,如"sda"、"nvme0n1"
char disk_name[DISK_NAME_LEN];
// 磁盘完整设备号,32位:高12位主设备号,低20位次设备号
dev_t devt;
// 主设备号:标识设备驱动类型,如8=SATA磁盘,259=NVMe磁盘
int major;
// 起始次设备号:标识同驱动下的不同设备
int first_minor;
// 次设备号数量:通常16,对应单磁盘最多15个分区
int minors;
// 磁盘的IO请求队列,所有IO请求的必经之路
struct request_queue *queue;
// 块设备操作函数集,驱动实现的标准接口
const struct block_device_operations *fops;
// 磁盘类型标志,如可移动设备、隐藏设备
unsigned long flags;
// 磁盘总容量,单位:512字节扇区(内核统一寻址单位)
sector_t capacity;
// 磁盘逻辑块大小,操作系统最小访问单元,通常512/4096字节
unsigned int logical_block_size;
// 磁盘物理块大小,硬件最小写入单元,现代存储默认4096字节
unsigned int physical_block_size;
// 硬件支持的最大IO大小,单位512字节扇区
unsigned int max_hw_sectors;
// 磁盘最优IO大小,对应RAID条带大小
unsigned int optimal_io_size;
// 分区表管理:磁盘所有分区的信息
struct disk_part_tbl __rcu *part_tbl;
// 整个磁盘对应的分区结构体(分区0)
struct hd_struct part0;
// 驱动私有数据,驱动可存储自定义上下文
void *private_data;
// 所属驱动模块,用于模块引用计数管理
struct module *owner;
// 对应设备模型的device结构体,支撑sysfs接口
struct device *to_dev;
// 热插拔、介质变化事件的工作队列
struct work_struct event_work;
} __randomize_layout;
2.1.2 核心字段深度解析
1.主设备号与次设备号
- 主设备号(major):标识块设备对应的驱动程序,同驱动管理的所有设备主设备号一致。典型值:
- 8:SATA/SCSI磁盘驱动,对应/dev/sd*设备;
- 259:NVMe磁盘驱动,对应/dev/nvme*设备;
- 253:Device Mapper驱动,对应LVM逻辑卷、dm设备;
- 次设备号(minor):标识同驱动下的不同设备/分区。如/dev/sda次设备号0,/dev/sda1次设备号1,以此类推;
- 设备号(dev_t):主+次设备号的组合,唯一标识一个块设备,ls -l /dev可查看设备的主次设备号。
2.块设备操作函数集 struct block_device_operations
这是块设备驱动必须实现的标准接口,是通用块层与驱动之间的桥梁,核心函数:
struct block_device_operations {
// 打开块设备
int (*open) (struct block_device *, fmode_t);
// 关闭块设备
void (*release) (struct gendisk *, fmode_t);
// 设备配置、参数查询的ioctl接口
int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
// 检查介质是否变化(如U盘插拔)
int (*media_changed) (struct gendisk *);
// 介质变化后重新验证磁盘
int (*revalidate_disk) (struct gendisk *);
// BIO提交入口,驱动实现的IO下发逻辑
void (*submit_bio)(struct bio *bio);
// 磁盘刷新操作,对应fsync,强制缓存数据落盘
int (*flush_disk)(struct gendisk *);
};
比如NVMe驱动会实现自己的nvme_fops函数集,处理NVMe设备的打开、关闭、IO提交等操作。
1.IO请求队列 struct request_queue
这是块设备的核心,所有发往该设备的IO请求都要经过这个队列,它包含了IO调度器、多队列配置、设备IO参数、队列限制等所有IO处理相关的信息,是后续章节的核心拆解对象。
2.容量与块大小字段
- capacity:磁盘总容量,单位是512字节扇区,无论物理磁盘的扇区大小是多少,内核统一用512字节扇区寻址,这是Linux块层的核心设计;
- logical_block_size/physical_block_size:逻辑块与物理块大小,是4K对齐的核心依据,未对齐的IO会触发磁盘的读-改-写操作,性能下降50%以上。
2.2 块设备实例:struct block_device
struct block_device(简称bdev)是内核中打开的块设备实例的抽象,对应/dev下的一个设备节点(如/dev/sda1),它与gendisk的核心关系:
- gendisk:代表整个物理磁盘,全局唯一,静态描述磁盘的全局属性;
- block_device:代表磁盘/分区的动态打开实例,一个gendisk可对应多个block_device(如整个磁盘sda、分区sda1/sda2各对应一个bdev)。
用户态打开块设备节点时,内核会创建对应的block_device实例,核心字段拆解:
struct block_device {
// 对应分区的hd_struct结构体
struct hd_struct *bd_part;
// 所属的全局gendisk实例
struct gendisk *bd_disk;
// 设备号,唯一标识这个块设备
dev_t bd_dev;
// 设备打开计数,记录有多少进程打开了这个设备
int bd_openers;
// 设备节点对应的inode
struct inode *bd_inode;
// 设备总大小,单位512字节扇区
sector_t bd_nr_sectors;
// 设备逻辑块大小
unsigned int bd_block_size;
// 设备互斥锁,保护元数据修改
struct mutex bd_mutex;
// 设备挂载计数,记录有多少文件系统挂载在这个设备上
int bd_mount_count;
};
2.3 分区抽象:struct hd_struct
struct hd_struct是内核对磁盘分区的抽象,每个分区对应一个实例(包括整个磁盘的分区0),存储了分区的起始扇区、大小、分区号、IO统计信息,是磁盘分区管理的核心结构。
核心字段拆解:
struct hd_struct {
// 分区起始扇区,单位512字节
sector_t start_sect;
// 分区总大小,单位512字节扇区
sector_t nr_sects;
// 分区号,sda1对应1,sda2对应2,以此类推
int partno;
// 分区IO统计信息,per-CPU变量,iostat工具的数据源
struct disk_stats __percpu *stats;
// 所属的全局gendisk实例
struct gendisk *disk;
// 引用计数
refcount_t refcnt;
};
2.4 四大核心结构的关联关系
以/dev/sda磁盘(含2个分区sda1/sda2)为例,核心结构的关联链路如下:
用户态/dev/sda1设备节点 → struct inode → struct block_device → struct hd_struct → struct gendisk → struct request_queue → 块设备驱动 → 物理磁盘
- 整个磁盘对应1个gendisk实例,1个block_device实例,1个hd_struct实例(分区0);
- 每个分区对应1个hd_struct实例,1个block_device实例;
- 所有分区与整个磁盘共享同一个gendisk的request_queue IO请求队列,所有IO请求都进入同一个队列处理。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)