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 结构体内存
相关推荐
DeeplyMind13 分钟前
linux VMA创建场景详解
linux·mmap·vma
扛枪的书生29 分钟前
Ansible 学习总结
linux
赵民勇44 分钟前
cut命令详解
linux·shell
闻道且行之1 小时前
Linux|CUDA与cuDNN下载安装全指南:默认/指定路径双方案+多CUDA环境一键切换
linux·运维·服务器
Ahtacca1 小时前
Linux环境下前后端分离项目(Spring Boot + Vue)手动部署全流程指南
linux·运维·服务器·vue.js·spring boot·笔记
_w_z_j_1 小时前
Linux----Socket编程基础
linux·运维·服务器
默|笙1 小时前
【Linux】进程控制(3)进程程序替换
android·linux·运维
xlxxy_2 小时前
abap 批量创建供应商
运维·开发语言·sap·abap·pp·mm
老前端的功夫2 小时前
TypeScript 全局类型声明:declare关键字的深度解析与实战
linux·前端·javascript·ubuntu·typescript·前端框架
赵民勇2 小时前
join命令使用指南与技巧
linux·shell