2. Linux IO栈全景:从VFS到块设备层、IO调度器与cgroup在栈中的位置、关键数据结构

理解Linux IO栈的全景是进行多租户IO性能调优的基础。本章将自上而下剖析IO栈的每一层,明确IO调度器与cgroup IO控制器在栈中的精确位置,并解析贯穿其中的关键数据结构。

2.1 Linux IO栈分层架构

Linux IO栈是一个高度模块化的分层体系,从应用程序的系统调用接口到物理磁盘的指令,共经历以下核心层次:

  1. VFS(虚拟文件系统)层:统一抽象所有文件系统,提供read()/write()/open()等标准接口。
  2. 具体文件系统层:如ext4、XFS、Btrfs,负责将VFS请求转换为块级别的逻辑操作。
  3. 块层(Block Layer):核心调度与排队场所,包含IO调度器、cgroup IO控制器、请求合并与排序逻辑。
  4. 设备驱动层:与具体硬件(NVMe、SCSI、virtio-blk)交互,发送命令并处理中断。
  5. 物理设备层:磁盘、SSD或NVMe设备,执行实际的数据读写。
关键认知: 多租户场景下的IO隔离与优先级控制,主要发生在块层。IO调度器和cgroup IO控制器在此层协同工作,决定哪个IO请求先被处理。
Linux IO栈分层架构 Linux IO栈分层架构 用户空间应用程序 (read/write) VFS (虚拟文件系统层) 具体文件系统 (ext4 / XFS / Btrfs) 块层 (Block Layer) IO调度器 | cgroup IO控制器 | 请求合并/排序 设备驱动层 (NVMe / SCSI / virtio-blk) 物理设备 (SSD / HDD / NVMe)

2.2 IO调度器与cgroup在栈中的精确位置

在块层内部,IO调度器和cgroup IO控制器(blkio)的协作关系如下:

  • cgroup IO控制器:位于块层入口处,负责根据cgroup配置(权重、上限、延迟)对IO请求进行分类和限流。
  • IO调度器:位于cgroup控制器之后,负责对已分类的IO请求进行排序、合并和分发。常见的调度器包括:mq-deadline、bfq、kyber、none。
数据流路径: 应用程序 → VFS → 文件系统 → cgroup blkio控制器 → IO调度器 → 驱动 → 设备。
IO调度器与cgroup在块层中的位置 IO调度器与cgroup在块层中的位置 来自文件系统 cgroup blkio 权重/上限/延迟 IO调度器 mq-deadline/BFQ/Kyber 驱动 多租户隔离的关键点 1. cgroup blkio 根据 cgroup 层级对 IO 请求打标签并限流。 2. IO 调度器根据标签进行公平排队或优先级排序。 3. 两者配合可实现:权重分配、IOPS上限、带宽上限、延迟保障。

2.3 关键数据结构

理解以下数据结构是深入调优的前提:

数据结构 所在层次 核心作用 关键字段
struct bio 块层 描述一次底层IO请求(扇区、方向、数据页) bi_sector, bi_size, bi_opf
struct request 块层/调度器 调度器管理的IO单元,可包含多个合并的bio q, rq_flags, __sector
struct request_queue 块层 每个块设备对应一个请求队列,挂载调度器和cgroup策略 elevator, mq_ops, blkcg
struct blkcg_gq cgroup blkio cgroup与请求队列的关联结构,存储权重与统计 blkcg, q, weight
struct elevator_queue IO调度器 调度器实例,包含调度算法特定的调度队列 type, ops, hash
常见误区: 初学者常混淆 struct bio 和 struct request。bio 是文件系统或VFS下发的原始IO单元,而 request 是经过块层合并、排序后的调度单元。一个 request 可以包含多个连续的 bio。

2.4 数据流全景:一次IO请求的完整旅程

以一次 read() 系统调用为例,数据流如下:

  1. VFS:根据文件描述符找到 inode,调用具体文件系统的 read_iter 方法。
  2. 文件系统:将文件逻辑偏移转换为块号,构造 struct bio,调用 submit_bio()。
  3. 块层入口:submit_bio() 进入通用块层,经过 blk_cgroup_bio_start() 进行cgroup记账。
  4. IO调度器:bio 被转换为 struct request,插入调度队列(如 deadline 的红黑树或 FIFO 队列)。
  5. 驱动提交:调度器通过 blk_mq_submit_bio() 将 request 发送给设备驱动。
  6. 硬件完成:设备完成读写,触发中断,驱动回调完成函数,逐层向上通知。
一次IO请求的完整旅程 一次IO请求的完整旅程 用户态 read() VFS 文件系统 (ext4) cgroup blkio 记账 IO调度器 (排队/合并) 驱动 → 物理设备 关键数据结构流转 bio: 文件系统构造 request: 调度器管理 blkcg_gq: cgroup关联

2.5 多队列块层(blk-mq)简介

现代Linux内核(3.13+)默认使用多队列块层(blk-mq)以应对NVMe等高速设备。其核心变化在于:

  • 单队列 → 多队列:每个CPU或硬件队列拥有独立的提交队列,减少锁竞争。
  • IO调度器适配:传统调度器(如deadline)被改造为 mq-deadline,BFQ和Kyber也原生支持多队列。
  • cgroup集成:cgroup blkio 在 blk-mq 中通过 blk_mq_ctx 和 blk_mq_hw_ctx 实现精细控制。
性能提示: 对于NVMe SSD,推荐使用 none 调度器(即不调度)或 kyber,以降低延迟。对于HDD或混合负载,mq-deadline 或 bfq 能提供更好的公平性。

2.6 本章小结

Linux IO栈从VFS到块设备层,每一层都有明确的职责。IO调度器和cgroup blkio控制器位于块层核心,共同决定了多租户环境下的IO隔离与性能。关键数据结构 bio、request、request_queue 和 blkcg_gq 是理解IO路径的钥匙。掌握这些全景知识,才能在后继章节中精准调优。