Linux I/O写数据全链路拆解

Linux I/O写数据全链路拆解

本文档详细拆解 Linux 系统中"数据写入"的全过程。从应用程序发起调用,经过内核各层级的处理,最终抵达物理硬件。理解这一链路对于排查 IO 高负载、优化数据库性能、以及理解数据一致性至关重要。

主要流程分为两个大阶段

  1. 前台阶段:应用发起写操作,数据写入到内存(很快)。
  2. 后台阶段:脏页回写,数据持久化到磁盘(较慢)。

第一阶段:从用户空间到Page Cache(前台异步写)

这个阶段通常由程序触发,不涉及磁盘硬件操作,瞬间完成。

  1. 用户空间:发起请求
    • 动作:程序调用 write()函数。
    • 内存状态
      • 用户态 buffer:数据目前放在进程的私有虚拟内存地址中。
    • CPU:指令执行,从用户态切换到内核态,触发中断进入内核。
  2. 文件系统:内存拷贝
    • 接单:sys_write-->vfs_write-->具体的文件系统
    • 内存分配
      • 内核在 page cache 中查找对应的页
      • 如果没找到,内核向伙伴系统申请分配一个内存页
    • 关键动作:CPU 亲自将数据将用户态 buffer 拷贝到内核态的 page cache 的物理页中。
    • 状态标记
      • 该page cache 被标记为脏页
      • inode 会记录该页的状态
    • 返回
      • 此时,write()调用结束,cpu 切换为用户态
      • 程序认为"写完了",继续执行下面的代码

第二阶段:从page cache到物理磁盘

这个阶段通常由内核线程或者 fsync 触发,真正的 I/O 旅行由此开始。

  1. 文件系统层:寻址与打包

    • 触发:回写机制启动(达到了预期内的系统脏页率,或者定时器到期)
    • 动作:文件系统开始处理脏页
    • 逻辑转换
      • 查账:查询 inode,找到文件偏移量 offset 0 对应的磁盘逻辑块号(LBA)(例如 LBA #1000)
      • 内存指派:文件系统创建个 bio 逻辑体
      • bio 填充:目标磁盘位置和大小、page cache 的物理内存地址、写操作、
      • 提交:将 bio 包裹扔给通用块层
  2. 通用块层:调度与队列

    • 接单:bio 包裹进入到请求队列
    • 内存操作:不涉及到数据拷贝,只操作 bio 结构体的指针
    • 动作
      • 合并:检查队列里是否有 LBA#999 的请求,如果有,就把现在的 LBA#1000拼接到他的后面,形成一个大的请求
      • 调度:如果是 HDD,按照电梯算法排序,如果是 NVMe 就直接透传
    • 分发:如果是多队列,根据 cpu ID 将请求放到对应的队列
  3. 设备驱动层:翻译与建图

    • 接单:NVMe 驱动从软件中队列中提取请求
    • 翻译:将通用的请求转换为 NVMe Write Command
    • 关键动作(DMA Mapping)
      • 硬件不懂虚拟地址,也不懂 page cache 的物理页是不连续的
      • 驱动建立一个散列表(SGL:Scatter Gather Lsit)
      • 内容:告诉硬件,数据在内存中的真实地址
      • 这个 SGL 的地址会被填入到 NVMe 的命令当中
    • 入列:将组装好的 NVMe 命令写入到内存中的提交队列
  4. 硬件交互:按门铃

    • 动作:驱动程序执行 MMIO 写操作
    • 细节:cpu 向 NVMe 控制器的 Doorbell 寄存器写入一个新的 Tail 指针
    • 含义:CPU 告诉 SSD 控制器,"SQ 队列中有新任务了,开起来干活!"
  5. 硬件层:DMA(Direct Memory Access) 搬运与落盘

    • 接单:SSD 控制器检测到 Doorbell 变化,从内存中的 SQ 队列中抓取命令。
    • 解析:控制器读取命令,拿到了SGL(内存地址表)
    • 数据搬运
      • 脱离 CPU:SSD 的 DMA 引擎启动
      • 总线传输:DMA 直接通过 PCIe 总线,从主机的 page cache 中把数据传输到 SSD 的内部 DRAM 缓存中。
      • 落盘:SSD 控制器通过 FTL 映射算法,将 DRAM 缓存中的数据写入到 NAND Flash 颗粒
  6. 中断与回调:结束

    • 硬件动作:数据传输完成后,SSD 控制器往内存中的完成队列写一个条目,并触发 MSI_X 中断
    • CPU 响应:CPU 暂停处理当前任务,跳转到驱动注册的 ISR(中断处理程序)
    • 驱动层
      • 读取 CQ:确认命令执行成功
      • 通知块层:回应完成
    • 通用块层:拆解通知包,找到 原始bio,结束这个 I/O
    • 文件系统
      • 解除脏标记:将 page cache 中对应的页面的 PG_dirty 标记清除,变为 PG_clean。
      • 释放资源:如果需要,将释放相关的 bio 结构体内存
相关推荐
chlk1232 天前
Linux文件权限完全图解:读懂 ls -l 和 chmod 755 背后的秘密
linux·操作系统
舒一笑2 天前
Ubuntu系统安装CodeX出现问题
linux·后端
改一下配置文件2 天前
Ubuntu24.04安装NVIDIA驱动完整指南(含Secure Boot解决方案)
linux
碳基沙盒2 天前
OpenClaw 多 Agent 配置实战指南
运维
深紫色的三北六号2 天前
Linux 服务器磁盘扩容与目录迁移:rsync + bind mount 实现服务无感迁移(无需修改配置)
linux·扩容·服务迁移
SudosuBash2 天前
[CS:APP 3e] 关于对 第 12 章 读/写者的一点思考和题解 (作业 12.19,12.20,12.21)
linux·并发·操作系统(os)
哈基咪怎么可能是AI3 天前
为什么我就想要「线性历史 + Signed Commits」GitHub 却把我当猴耍 🤬🎙️
linux·github
十日十行3 天前
Linux和window共享文件夹
linux
木心月转码ing4 天前
WSL+Cpp开发环境配置
linux
蝎子莱莱爱打怪5 天前
Centos7中一键安装K8s集群以及Rancher安装记录
运维·后端·kubernetes