任何涉及磁盘读写的应用(MySQL、Kafka、ES、Redis AOF 等),底层都离不开这套 IO 机制。这是一篇通用型的操作系统 IO 笔记。

概念:刷盘的两步

"刷盘"这个词在日常使用中比较模糊,在 OS 层面实际分两个步骤:

完整数据流转路径

数据从应用写入磁盘、从磁盘发送到网络的完整路径,涉及用户态 ↔ 内核态切换、DMA 拷贝等概念。以下三张图分别展示三种场景。

① 写路径(write 方向)

数据从用户进程 → 内核 Page Cache → 磁盘。

   用户态                 内核态              硬件
┌──────────┐ write  ┌─────────────┐ fsync ┌──────┐
│ 用户缓冲区 │──────► │  Page Cache │ ────► │ 磁盘  │
│ (stdio)  │ flush  │ (内核缓冲区)  │       │      │
└──────────┘        └─────────────┘       └──────┘
  • write() / flush():用户态 → 内核态(数据到达 Page Cache,未落盘)

  • fsync():Page Cache → 磁盘(真正落盘,持久化)


② 读 + 发送路径(sendfile / 零拷贝)

数据从磁盘 → 内核 Page Cache → 直接到网卡,不经过用户态,减少拷贝次数。

  硬件          内核态            硬件
┌──────┐ DMA ┌───────────┐ DMA ┌──────┐
│ 磁盘  │ ───►│ PageCache │ ───►│ 网卡  │
└──────┘     │(内核缓冲区) │     └──────┘
             └───────────┘
                ▲
       sendfile()(不经过用户态)
         ┌──────┴─────┐
         │  用户进程   │ ←只发命令,无数据
         └────────────┘
  • • 只产生 2 次 DMA 拷贝(磁盘→Page Cache,Page Cache→网卡)

  • • 用户进程仅发起 sendfile() 系统调用,不参与数据搬运


③ 传统 read + write 路径(4 次拷贝)

数据从磁盘 → 内核 → 用户态 → 内核 → 网卡,经历 4 次拷贝,CPU 参与其中 2 次。

  硬件        内核态         用户态          内核态         硬件
┌──────┐   ┌─────────┐   ┌──────────┐   ┌──────────┐   ┌──────┐
│ 磁盘  │──►│PageCache│──►│用户缓冲区 │──►│SocketBuf  │──►│ 网卡 │
└──────┘   └─────────┘   └──────────┘   └──────────┘   └──────┘
 DMA        ① DMA          ② CPU          ③ CPU        ④ DMA

在这里插入图片描述
对比上图:sendfile 省去了 ②和③ 两次 CPU 拷贝,且无需切换用户态/内核态,因此称为"零拷贝"。

flush vs fsync 对比

在这里插入图片描述
一句话总结:flush 是 C 库 → 内核,fsync 是内核 → 磁盘。只有 fsync 才算真正落盘。

场景速查:各产品 IO 策略对比

在这里插入图片描述

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐