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 结构体内存
相关推荐
我要精通C++2 小时前
从源码看nginx的缓存功能
运维·nginx·缓存
_OP_CHEN2 小时前
【Git原理与使用】(四)Git 远程操作与标签管理全解析:从分布式协作到版本标记最全攻略
linux·运维·分布式·git·git远程仓库·企业级组件·git标签管理
艾莉丝努力练剑2 小时前
【Linux基础开发工具 (七)】Git 版本管理全流程与 GDB / CGDB 调试技巧
大数据·linux·运维·服务器·git·安全·elasticsearch
shandianchengzi2 小时前
【记录】ARM|Ubuntu 24 快速安装 arm-none-eabi-gdb 及 QEMU 调试实战
linux·arm开发·ubuntu·arm·qemu
weixin_307779132 小时前
Jenkins SSH Build Agents 插件详解:远程构建的利器
运维·开发语言·架构·ssh·jenkins
学困昇2 小时前
Linux 进程概念与内存管理详解(含冯诺依曼体系结构、环境变量、调度算法)
linux·c语言·开发语言·网络·数据结构·c++
靈龍靈2 小时前
ELBK部署
运维·ci/cd·jenkins
为什么要内卷,摆烂不香吗2 小时前
sed 流编辑器练习自用
linux·运维·编辑器
智象科技2 小时前
从资源到业务:运维监控体系的差异
大数据·运维·一体化运维·智能运维·多云管理