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 结构体内存
相关推荐
maosheng11467 小时前
RHCSA的第一次作业
linux·运维·服务器
wifi chicken8 小时前
Linux 端口扫描及拓展
linux·端口扫描·网络攻击
旺仔.2918 小时前
Linux 信号详解
linux·运维·网络
放飞梦想C8 小时前
CPU Cache
linux·cache
Hoshino.419 小时前
基于Linux中的数据库操作——下载与安装(1)
linux·运维·数据库
恒创科技HK10 小时前
通用型云服务器与计算型云服务器:您真正需要哪些配置?
运维·服务器
吴佳浩 Alben10 小时前
GPU 生产环境实践:硬件拓扑、显存管理与完整运维体系
运维·人工智能·pytorch·语言模型·transformer·vllm
播播资源11 小时前
CentOS系统 + 宝塔面板 部署 OpenClaw源码开发版完整教程
linux·运维·centos
源远流长jerry11 小时前
在 Ubuntu 22.04 上配置 Soft-RoCE 并运行 RDMA 测试程序
linux·服务器·网络·tcp/ip·ubuntu·架构·ip
学不完的11 小时前
Docker数据卷管理及优化
运维·docker·容器·eureka