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 结构体内存
相关推荐
江湖有缘22 分钟前
Docker实战 | 使用Docker部署Donetick任务与家务管理应用
运维·docker·容器
实心儿儿31 分钟前
Linux —— 应用层协议HTTP
linux·网络·http
流星白龙35 分钟前
【Docker】4.NameSpace空间隔离实战
java·运维·docker
有续技术1 小时前
哈斯 (Haas) 机床 IP、端口号配置内容总结
运维·服务器
小张同学a.2 小时前
LAMP架构2
linux·运维·网络·架构·负载均衡
千河殇 多想和她2 小时前
如何在Reaonix中使用CodeGraph以及CodeGraph效果实测经验分享
linux·运维·ubuntu
VortMall2 小时前
出海引流运维双升级|VortMall微服务商城系统v1.3.12 升级更新
运维·微服务·facebook
心念枕惊2 小时前
新写了个直播录制工具,可录制抖音快手斗鱼直播
运维·服务器·数据库
躺不平的理查德2 小时前
CMAKE备忘录
linux·运维·服务器
9624562 小时前
跨域与私有网络访问限制排查实录
运维·服务器·网络