6.1I/O系统的功能、模型和接口

在这里插入图片描述

### ✅ 回答问题:Linux 内核 6.1 中的 `block` 子系统架构与核心机制详解 --- **简要答案**: 在 Linux 内核 **6.1** 版本中,`block`(块设备)子系统是 I/O 栈的核心组件,负责管理所有面向块的存储设备(如硬盘、SSD、虚拟磁盘等)。它通过统一的接口抽象物理逻辑设备,支持多队列 I/O 调度(`blk-mq`)、请求排队、分区解析、I/O 合并与延迟控制,并为上层文件系统(ext4, XFS 等)提供可靠的读写服务。 > 🧩 主要位于源码路径:`drivers/block/`, `block/`, `include/linux/blk*.h` --- ## 🏗️ 一、整体架构图(Linux 6.1) ``` +---------------------+ | 文件系统层 | ← ext4, XFS, Btrfs → 发出 bio +---------------------+ ↓ submit_bio() +---------------------+ | Generic Block Layer | ← 处理 BIO(I/O 请求向量) | | | - bio_alloc() | | - merge_bios() | | - partitioning | +---------------------+ ↓ 提交到请求队列 +---------------------+ | blk-mq 框架 | ← 多队列调度(主要入口) | | | - blk_mq_make_request| | - soft queue (hctx) | | - hardware context | +---------------------+ ↓ 派发给驱动 +---------------------+ | 块设备驱动程序 | ← NVMe, virtio-blk, SATA(AHCI), MMC | (Device Driver) | 构造硬件命令并发送 +---------------------+ ↓ +---------------------+ | 物理存储设备 | ← SSD, HDD, eMMC, SD Card +---------------------+ ``` --- ## 🔍 二、关键数据结构(Kernel 6.1) ### 1. `struct bio` —— I/O 请求的基本单位 代表一个“分散-聚集”式的 I/O 请求: ```c struct bio { sector_t bi_iter.bi_sector; // 起始扇区(LBA) unsigned int bi_iter.bi_size; // 字节数 struct bvec_iter bi_iter; // 当前操作位置 struct bio_vec *bi_io_vec; // 指向 bvec 数组 unsigned short bi_vcnt; // 实际使用的 bvec 数量 unsigned short bi_max_vecs; // 最大支持的段数 struct block_device *bi_disk; // 目标设备 struct gendisk *bi_disk; bio_end_io_t *bi_end_io; // 完成回调函数 void *bi_private; // 私有数据(如 fs info) }; ``` > ⚠️ 注意:从内核 5.x 开始,`bio` 不再包含嵌入式 `bvec` 数组,而是动态分配。 #### 示例:分配并初始化一个 `bio` ```c struct bio *bio = bio_alloc(GFP_NOIO, 4); // 支持最多 4 个段 bio_set_dev(bio, bdev); bio->bi_iter.bi_sector = 1024; // LBA=1024 bio_add_page(bio, page, 4096, 0); // 添加一页数据 bio->bi_end_io = my_completion_fn; // 设置完成处理函数 submit_bio(REQ_OP_READ, bio); // 提交读请求 ``` --- ### 2. `struct request_queue` `blk-mq` 在 Linux 6.1 中,旧的单队列模型已被完全弃用,全面采用 **`blk-mq`(Block Multi-Queue)** 架构。 #### 核心结构: ```c struct request_queue { struct blk_mq_ops *mq_ops; // 驱动实现的操作集 struct blk_mq_ctx __percpu *queue_ctx; // 每 CPU 上下文(soft queue) struct blk_mq_hw_ctx **queue_hw_ctx; // 硬件上下文数组 unsigned int nr_hw_queues; // 硬件队列数量 unsigned int queue_depth; // 每个硬件队列深度 struct elevator_queue *elevator; // I/O 调度器(可选) ... }; ``` #### 工作流程: 1. 进程在某个 CPU 上提交 `bio` 2. 映射到该 CPU 对应的 `blk_mq_ctx` 3. 选择一个 `blk_mq_hw_ctx`(可能绑定特定 CPU 或硬件通道) 4. 将请求加入 `hw_ctx` 的队列 5. 触发驱动的 `.queue_rq()` 方法执行派发 --- ### 3. `struct request`(已逐步弱化) 在 `blk-mq` 模型中,`request` 不再是必须存在的对象。许多现代驱动(如 NVMe)直接使用 `struct scsi_cmnd` 或自定义命令结构。 但在一些传统驱动或需要重试/排序时仍会创建: ```c struct request { struct request_queue *q; unsigned int cmd_flags; // REQ_OP_READ / REQ_OP_WRITE struct bio *bio, *biotail; // 链接的 bio 列表 struct list_head queuelist; // 在调度队列中的链表节点 }; ``` --- ### 4. `struct gendisk` —— 通用磁盘描述符 表示一个完整的块设备及其分区信息: ```c struct gendisk { int major; // 主设备号 int first_minor; int minors; // 次设备号范围(1: 只有整盘;>1: 支持分区) char disk_name[DISK_NAME_LEN]; // 名称如 "sda" struct disk_part_tbl __rcu *part_tbl; // 分区表(索引从 0 开始) const struct block_device_operations *fops; // open/release/ioctl 等 struct device part0; // 整个设备对应的 device int flags; }; ``` > 分区设备 `/dev/sda1` 实际对应 `part_tbl->part[1]`,其 `bdev` 会被创建。 --- ## ⚙️ 三、I/O 请求生命周期(以读为例) ```text 1. VFS → ext4_readpage() 2. create BIO for page (sector + len) 3. submit_bio(REQ_OP_READ, bio) 4. generic_make_request() ├→ 查找目标 gendisk 分区 ├→ 更新统计信息 (part_stat_inc()) └→ 调用 q->make_request_fn() → 默认为 blk_mq_make_request() 5. blk_mq_make_request() ├→ 获取当前 CPU 的 soft queue (ctx) ├→ 分配 hctx(可能基于 CPU 绑定) ├→ 加入 hctx->rq_list └→ 标记需要处理 (__blk_mq_sched_insert_request) 6. 触发运行(软中断或 tasklet) 7. 调用驱动 .queue_rq() 函数(如 nvme_queue_rq) 8. 驱动将命令写入寄存器或共享内存(如 NVMe doorbell) 9. 设备完成操作后触发中断 10. 中断处理函数调用 bio_endio(bio) → 执行 bi_end_io 回调 11. 页面被标记为 unlocked → wake_up_page() ``` --- ## 💡 四、编程示例:在模块中创建虚拟块设备(vbd) 以下是一个可在 Linux 6.1 上运行的简单虚拟块设备模块: ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/blkdev.h> #include <linux/genhd.h> #include <linux/bio.h> #define VBD_SIZE (16 * 1024 * 1024) // 16MB #define VBD_SECTOR_SIZE 512 #define VBD_NUM_SECTORS (VBD_SIZE / VBD_SECTOR_SIZE) static struct request_queue *vbd_queue; static struct gendisk *vbd_disk; static u8 *vbd_data; // 模拟存储空间 // 请求处理函数(仅用于调试,实际应使用 blk-mq) static blk_status_t vbd_submit_bio(struct bio *bio) { struct bio_vec bvec; struct bvec_iter iter; sector_t sector = bio->bi_iter.bi_sector; if (sector + (bio_bytes(bio) >> 9) > VBD_NUM_SECTORS) { bio_io_error(bio); return BLK_STS_IOERR; } bio_for_each_segment(bvec, bio, iter) { void *daddr = vbd_data + (sector << 9); void *src = kmap_atomic(bvec.bv_page) + bvec.bv_offset; if (bio_data_dir(bio) == READ) memcpy(src, daddr, bvec.bv_len); else memcpy(daddr, src, bvec.bv_len); kunmap_atomic(src); sector += bvec.bv_len >> 9; } bio_endio(bio); // 完成请求 return BLK_STS_OK; } static int __init vbd_init(void) { int ret; // 分配模拟内存 vbd_data = kzalloc(VBD_SIZE, GFP_KERNEL); if (!vbd_data) return -ENOMEM; // 分配请求队列(使用 blk-mq 默认行为) vbd_queue = blk_alloc_queue(NUMA_NO_NODE); if (!vbd_queue) { ret = -ENOMEM; goto out_free; } blk_queue_make_request(vbd_queue, vbd_submit_bio); blk_queue_logical_block_size(vbd_queue, VBD_SECTOR_SIZE); // 分配磁盘结构 vbd_disk = alloc_disk(16); // 支持 16 个分区 if (!vbd_disk) { ret = -ENOMEM; goto out_cleanup_queue; } vbd_disk->major = 0; // 动态分配主设备号 vbd_disk->first_minor = 0; vbd_disk->minors = 16; strcpy(vbd_disk->disk_name, "vbd"); vbd_disk->fops = &vbd_fops; vbd_disk->queue = vbd_queue; set_capacity(vbd_disk, VBD_NUM_SECTORS); add_disk(vbd_disk); printk(KERN_INFO "Virtual block device %s (%d MB) registered\n", vbd_disk->disk_name, VBD_SIZE >> 20); return 0; out_cleanup_queue: blk_cleanup_queue(vbd_queue); out_free: kfree(vbd_data); return ret; } static void __exit vbd_exit(void) { del_gendisk(vbd_disk); put_disk(vbd_disk); blk_cleanup_queue(vbd_queue); kfree(vbd_data); printk(KERN_INFO "vbd module removed\n"); } // 必须定义 file_operations(即使为空) static const struct block_device_operations vbd_fops = { .owner = THIS_MODULE, .report_zones = NULL, }; module_init(vbd_init); module_exit(vbd_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Simple Virtual Block Device for Linux 6.1"); ``` 📌 编译 Makefile: ```makefile obj-m += vbd.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean install: sudo insmod vbd.ko remove: sudo rmmod vbd ``` 📌 加载后查看设备: ```bash dmesg | tail lsblk | grep vbd ``` 输出示例: ``` vbd 259:0 0 16M 0 disk ``` --- ## 🛠️ 五、常用调试工具与接口 | 工具 | 用途 | |------|------| | `cat /proc/partitions` | 查看当前所有分区 | | `lsblk` | 列出块设备树状结构 | | `iostat -x 1` | 实时监控 I/O 性能 | | `blktrace` | 跟踪块设备请求路径 | | `echo "dump" > /sys/block/sda/trace/act_mask` | 启用跟踪(需配置) | --- ## 📈 六、Linux 6.1 新特性(相比早期版本) | 特性 | 描述 | |------|------| | 更完善的 `bio` 生命周期管理 | 使用 refcount 控制释放 | | 支持 `BIO_FLAG_NOWAIT` | 非阻塞 I/O 提交(配合 io_uring 使用) | | blk-crypto inline support | 为 UFS/EMMC 提供原生加密卸载 | | blk-mq polling mode 改进 | 更低延迟轮询机制 | | REQ_OP_ZONE_APPEND 支持 | 用于 zoned block devices(如 SMR 硬盘) | --- ### 💎 总结 | 组件 | 作用 | |------|------| | `bio` | I/O 请求载体,支持 scatter-gather | | `request_queue` + `blk-mq` | 多队列调度框架,提升并发性能 | | `gendisk` | 块设备抽象,包含分区信息 | | `block_device_operations` | 用户空间操作钩子(open/ioctl) | | `AF_ALG` / `dm-crypt` | 上层应用利用 block+crypto 实现透明加密 | --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值