Linux文件IO底层原理详解

Linux文件IO底层原理详解

执行 cat test.txt 或调用 read()/write() 时,数据往往并未直接落在磁盘上 :读路径经过 Page Cache ,写路径常先标记脏页 再由内核异步刷盘。理解 VFS、inode、文件描述符、页缓存与系统调用 的分工,才能解释「write 已成功但断电丢数据」「第二次读同一文件更快」等现象,并为高并发下的 epoll、零拷贝 选型打底。

速览

  • 一切皆文件 :普通文件、设备、管道、套接字等经 VFS 统一抽象。
  • inode :文件元数据与数据块指针;目录 是「文件名 → inode」映射;FD 是进程内的打开实例(偏移、模式)。
  • :磁盘 → Page Cache → 用户缓冲区(通常两次拷贝); :先写 Page Cache,writeback 异步落盘。
  • 持久化write 返回 ≠ 落盘;需 fsync/fdatasyncO_SYNC 等才更强保证。
  • 进阶 :阻塞/非阻塞/多路复用/AIO/零拷贝;高并发文件发送常配合 sendfilemmap
text 复制代码
逻辑链条:VFS 抽象 → FD 操作 inode → open/read/write/close
         → Page Cache 加速 → syscall 进入内核 → 多种 IO 模型

阅读导航

全文约 6000 字,可按目标跳读,不必一次读完。

目标 建议章节 预计时间
建立心智模型(VFS / inode / FD / 读写路径) [1](#目标 建议章节 预计时间 建立心智模型(VFS / inode / FD / 读写路径) 1–7 ~15 分钟 缓存、syscall、性能 8、9、13 ~10 分钟 面试 / 复盘 12、14 ~5 分钟 高并发与零拷贝 10、11 ~10 分钟)--[7](#目标 建议章节 预计时间 建立心智模型(VFS / inode / FD / 读写路径) 1–7 ~15 分钟 缓存、syscall、性能 8、9、13 ~10 分钟 面试 / 复盘 12、14 ~5 分钟 高并发与零拷贝 10、11 ~10 分钟) ~15 分钟
缓存、syscall、性能 [8](#目标 建议章节 预计时间 建立心智模型(VFS / inode / FD / 读写路径) 1–7 ~15 分钟 缓存、syscall、性能 8、9、13 ~10 分钟 面试 / 复盘 12、14 ~5 分钟 高并发与零拷贝 10、11 ~10 分钟)、[9](#目标 建议章节 预计时间 建立心智模型(VFS / inode / FD / 读写路径) 1–7 ~15 分钟 缓存、syscall、性能 8、9、13 ~10 分钟 面试 / 复盘 12、14 ~5 分钟 高并发与零拷贝 10、11 ~10 分钟)、[13](#目标 建议章节 预计时间 建立心智模型(VFS / inode / FD / 读写路径) 1–7 ~15 分钟 缓存、syscall、性能 8、9、13 ~10 分钟 面试 / 复盘 12、14 ~5 分钟 高并发与零拷贝 10、11 ~10 分钟) ~10 分钟
面试 / 复盘 [12](#目标 建议章节 预计时间 建立心智模型(VFS / inode / FD / 读写路径) 1–7 ~15 分钟 缓存、syscall、性能 8、9、13 ~10 分钟 面试 / 复盘 12、14 ~5 分钟 高并发与零拷贝 10、11 ~10 分钟)、[14](#目标 建议章节 预计时间 建立心智模型(VFS / inode / FD / 读写路径) 1–7 ~15 分钟 缓存、syscall、性能 8、9、13 ~10 分钟 面试 / 复盘 12、14 ~5 分钟 高并发与零拷贝 10、11 ~10 分钟) ~5 分钟
高并发与零拷贝 [10](#目标 建议章节 预计时间 建立心智模型(VFS / inode / FD / 读写路径) 1–7 ~15 分钟 缓存、syscall、性能 8、9、13 ~10 分钟 面试 / 复盘 12、14 ~5 分钟 高并发与零拷贝 10、11 ~10 分钟)、[11](#目标 建议章节 预计时间 建立心智模型(VFS / inode / FD / 读写路径) 1–7 ~15 分钟 缓存、syscall、性能 8、9、13 ~10 分钟 面试 / 复盘 12、14 ~5 分钟 高并发与零拷贝 10、11 ~10 分钟) ~10 分钟

第 9 节含 x86_64 寄存器示意,不关心汇编者可只读 9.1 时序图 + 9.3 性能含义 ,细节见 [附录 A](#附录 A)。


目录

一、对象与抽象

  • [1. VFS 与「一切皆文件」](#1. VFS 与「一切皆文件」)
  • [2. inode、目录与链接](#2. inode、目录与链接)
  • [3. 文件描述符与 struct file](#3. 文件描述符与 struct file)

二、一次 IO 的生命周期

  • [4. open:路径解析到分配 FD](#4. open:路径解析到分配 FD)
  • [5. read:Page Cache 与两次拷贝](#5. read:Page Cache 与两次拷贝)
  • [6. write、脏页与持久化](#6. write、脏页与持久化)
  • [7. close 与 FD 泄露](#7. close 与 FD 泄露)

三、缓存与进入内核

  • [8. 三层缓冲与数据路径](#8. 三层缓冲与数据路径)
  • [9. 系统调用路径](#9. 系统调用路径)

四、IO 模型与延伸

  • [10. 五种 IO 模型对照](#10. 五种 IO 模型对照)
  • [11. read 与 mmap、零拷贝](#11. read 与 mmap、零拷贝)
  • [12. 常见误区](#12. 常见误区)
  • [13. 性能调优要点](#13. 性能调优要点)
  • [14. 速查与验证](#14. 速查与验证)

附录

  • [附录 A:x86_64 系统调用传参示意](#附录 A:x86_64 系统调用传参示意)

1. VFS 与「一切皆文件」

Linux 通过 VFS(Virtual File System) 为 ext4、xfs、btrfs、tmpfs 等具体文件系统提供统一接口。应用调用 open/read/write 时,先进入 VFS,再由具体 file_operations 访问 inode 与底层存储。

1.1 VFS 在栈中的位置

#mermaid-svg-pHwazJilOuTUz136{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-pHwazJilOuTUz136 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pHwazJilOuTUz136 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pHwazJilOuTUz136 .error-icon{fill:#552222;}#mermaid-svg-pHwazJilOuTUz136 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pHwazJilOuTUz136 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pHwazJilOuTUz136 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pHwazJilOuTUz136 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pHwazJilOuTUz136 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pHwazJilOuTUz136 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pHwazJilOuTUz136 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pHwazJilOuTUz136 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pHwazJilOuTUz136 .marker.cross{stroke:#333333;}#mermaid-svg-pHwazJilOuTUz136 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pHwazJilOuTUz136 p{margin:0;}#mermaid-svg-pHwazJilOuTUz136 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pHwazJilOuTUz136 .cluster-label text{fill:#333;}#mermaid-svg-pHwazJilOuTUz136 .cluster-label span{color:#333;}#mermaid-svg-pHwazJilOuTUz136 .cluster-label span p{background-color:transparent;}#mermaid-svg-pHwazJilOuTUz136 .label text,#mermaid-svg-pHwazJilOuTUz136 span{fill:#333;color:#333;}#mermaid-svg-pHwazJilOuTUz136 .node rect,#mermaid-svg-pHwazJilOuTUz136 .node circle,#mermaid-svg-pHwazJilOuTUz136 .node ellipse,#mermaid-svg-pHwazJilOuTUz136 .node polygon,#mermaid-svg-pHwazJilOuTUz136 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pHwazJilOuTUz136 .rough-node .label text,#mermaid-svg-pHwazJilOuTUz136 .node .label text,#mermaid-svg-pHwazJilOuTUz136 .image-shape .label,#mermaid-svg-pHwazJilOuTUz136 .icon-shape .label{text-anchor:middle;}#mermaid-svg-pHwazJilOuTUz136 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pHwazJilOuTUz136 .rough-node .label,#mermaid-svg-pHwazJilOuTUz136 .node .label,#mermaid-svg-pHwazJilOuTUz136 .image-shape .label,#mermaid-svg-pHwazJilOuTUz136 .icon-shape .label{text-align:center;}#mermaid-svg-pHwazJilOuTUz136 .node.clickable{cursor:pointer;}#mermaid-svg-pHwazJilOuTUz136 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pHwazJilOuTUz136 .arrowheadPath{fill:#333333;}#mermaid-svg-pHwazJilOuTUz136 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pHwazJilOuTUz136 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pHwazJilOuTUz136 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pHwazJilOuTUz136 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pHwazJilOuTUz136 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pHwazJilOuTUz136 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pHwazJilOuTUz136 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pHwazJilOuTUz136 .cluster text{fill:#333;}#mermaid-svg-pHwazJilOuTUz136 .cluster span{color:#333;}#mermaid-svg-pHwazJilOuTUz136 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-pHwazJilOuTUz136 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pHwazJilOuTUz136 rect.text{fill:none;stroke-width:0;}#mermaid-svg-pHwazJilOuTUz136 .icon-shape,#mermaid-svg-pHwazJilOuTUz136 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pHwazJilOuTUz136 .icon-shape p,#mermaid-svg-pHwazJilOuTUz136 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pHwazJilOuTUz136 .icon-shape .label rect,#mermaid-svg-pHwazJilOuTUz136 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pHwazJilOuTUz136 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pHwazJilOuTUz136 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pHwazJilOuTUz136 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 块层与设备
具体文件系统
VFS 层
用户态
应用程序

open read write
VFS 统一入口

路径解析 / 权限 / fd 表
ext4 / xfs / btrfs ...
proc / sysfs 伪 FS
socket 等特殊 file_ops
块层 / 驱动
磁盘 / SSD

访问对象 在 VFS 下的表现
普通文件 常规 inode + 数据块 / extent
块/字符设备 /dev/sda 等,走块/字符设备驱动
管道、套接字 专用 file_operations,无传统磁盘块
/proc/sys 伪文件系统,内容由内核按需生成

统一抽象的好处:同一套 API 服务磁盘、网络、设备;代价是需分清背后的真实类型------例如 socket 的 read 等待的是套接字缓冲区,不会走磁盘 Page Cache。

回顾 :VFS 是「总前台」------应用只认 open/read/write,具体是磁盘文件还是设备由下层 file_operations 决定。


2. inode、目录与链接

2.1 inode

inode 保存文件元数据,典型包括:

字段类 内容
身份 inode 号、文件类型(普通/目录/链接/设备...)
权限 uid/gid、rwx 位
大小与时间 st_sizeatime/mtime/ctime
数据定位 块指针或 extent 树(大文件)
链接计数 硬链接数量

文件名不在 inode 里。删除「文件名」只是删掉目录项;inode 与数据块在链接计数为 0 后才回收。

2.2 目录与 dentry

目录本质是 「文件名 → inode 号」 的映射表。

text 复制代码
  用户路径:  /home/user/test.txt
                    │
                    ▼
  ┌─────────────────────────────────────┐
  │ dentry 缓存(目录项,加速路径查找)    │
  └─────────────────────────────────────┘
                    │
                    ▼
              inode #12345  ──►  数据块 / extent

路径解析从根 / 起逐级查 dentry;命中缓存可避免反复读磁盘上的目录块。

2.3 硬链接与符号链接

类型 机制 ls -l 特征 删除影响
硬链接 同 inode,多目录项 相同 inode 号、链接数 +1 删一名仍保留 inode,直至链接数为 0
符号链接 单独 inode,内容为路径字符串 类型为 l,箭头指向目标 目标删了则悬空(broken symlink)

2.4 命令速览:看清 inode

bash 复制代码
ls -li test.txt          # 第一列为 inode 号
stat test.txt            # 详细元数据
find . -inum 12345       # 按 inode 反查硬链接

3. 文件描述符与 struct file

FD 是进程内的非负整数,指向内核 struct file(一次「打开」),而不是 inode 本身。

3.1 四元关系

#mermaid-svg-Dzyo0riSgcL5C3AB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Dzyo0riSgcL5C3AB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Dzyo0riSgcL5C3AB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Dzyo0riSgcL5C3AB .error-icon{fill:#552222;}#mermaid-svg-Dzyo0riSgcL5C3AB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Dzyo0riSgcL5C3AB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Dzyo0riSgcL5C3AB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Dzyo0riSgcL5C3AB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Dzyo0riSgcL5C3AB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Dzyo0riSgcL5C3AB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Dzyo0riSgcL5C3AB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Dzyo0riSgcL5C3AB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Dzyo0riSgcL5C3AB .marker.cross{stroke:#333333;}#mermaid-svg-Dzyo0riSgcL5C3AB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Dzyo0riSgcL5C3AB p{margin:0;}#mermaid-svg-Dzyo0riSgcL5C3AB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Dzyo0riSgcL5C3AB .cluster-label text{fill:#333;}#mermaid-svg-Dzyo0riSgcL5C3AB .cluster-label span{color:#333;}#mermaid-svg-Dzyo0riSgcL5C3AB .cluster-label span p{background-color:transparent;}#mermaid-svg-Dzyo0riSgcL5C3AB .label text,#mermaid-svg-Dzyo0riSgcL5C3AB span{fill:#333;color:#333;}#mermaid-svg-Dzyo0riSgcL5C3AB .node rect,#mermaid-svg-Dzyo0riSgcL5C3AB .node circle,#mermaid-svg-Dzyo0riSgcL5C3AB .node ellipse,#mermaid-svg-Dzyo0riSgcL5C3AB .node polygon,#mermaid-svg-Dzyo0riSgcL5C3AB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Dzyo0riSgcL5C3AB .rough-node .label text,#mermaid-svg-Dzyo0riSgcL5C3AB .node .label text,#mermaid-svg-Dzyo0riSgcL5C3AB .image-shape .label,#mermaid-svg-Dzyo0riSgcL5C3AB .icon-shape .label{text-anchor:middle;}#mermaid-svg-Dzyo0riSgcL5C3AB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Dzyo0riSgcL5C3AB .rough-node .label,#mermaid-svg-Dzyo0riSgcL5C3AB .node .label,#mermaid-svg-Dzyo0riSgcL5C3AB .image-shape .label,#mermaid-svg-Dzyo0riSgcL5C3AB .icon-shape .label{text-align:center;}#mermaid-svg-Dzyo0riSgcL5C3AB .node.clickable{cursor:pointer;}#mermaid-svg-Dzyo0riSgcL5C3AB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Dzyo0riSgcL5C3AB .arrowheadPath{fill:#333333;}#mermaid-svg-Dzyo0riSgcL5C3AB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Dzyo0riSgcL5C3AB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Dzyo0riSgcL5C3AB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Dzyo0riSgcL5C3AB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Dzyo0riSgcL5C3AB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Dzyo0riSgcL5C3AB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Dzyo0riSgcL5C3AB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Dzyo0riSgcL5C3AB .cluster text{fill:#333;}#mermaid-svg-Dzyo0riSgcL5C3AB .cluster span{color:#333;}#mermaid-svg-Dzyo0riSgcL5C3AB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Dzyo0riSgcL5C3AB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Dzyo0riSgcL5C3AB rect.text{fill:none;stroke-width:0;}#mermaid-svg-Dzyo0riSgcL5C3AB .icon-shape,#mermaid-svg-Dzyo0riSgcL5C3AB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Dzyo0riSgcL5C3AB .icon-shape p,#mermaid-svg-Dzyo0riSgcL5C3AB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Dzyo0riSgcL5C3AB .icon-shape .label rect,#mermaid-svg-Dzyo0riSgcL5C3AB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Dzyo0riSgcL5C3AB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Dzyo0riSgcL5C3AB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Dzyo0riSgcL5C3AB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 内核
进程
fd 表

fd=3
struct file

f_pos 偏移

f_mode 标志
inode

元数据+数据块
路径名 test.txt
dentry

概念 含义 生命周期
inode 文件实体 链接计数为 0 时销毁
struct file 某次打开:offset、标志、操作表 close 且引用计数为 0
FD 进程 fd 表中的槽位 close 或进程退出
dentry 路径分量缓存 由 LRU 等回收

3.2 进程间与 dup

场景 inode struct file FD 编号
两进程各 open 同一文件 共享 各一份 各自独立
dup(fd) 共享 共享 不同号,偏移联动
fork 共享 共享(引用计数 +1) 子进程继承相同编号
  • 默认 0/1/2 :stdin/stdout/stderr;新打开通常从 3 起。
  • FD 编号、类型、/proc/PID/fd、上限见《Linux文件描述符FD机制深度解析》。

本部分回顾(一 · 对象与抽象)inode 是文件是谁,目录名 只是索引;FD 是进程手里那张「办事号」,背后连着一次打开(struct file)和同一个 inode。


4. open:路径解析到分配 FD

c 复制代码
int fd = open("test.txt", O_RDONLY);

4.1 内核步骤

#mermaid-svg-iOTFlabqq4NBqBwY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-iOTFlabqq4NBqBwY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-iOTFlabqq4NBqBwY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-iOTFlabqq4NBqBwY .error-icon{fill:#552222;}#mermaid-svg-iOTFlabqq4NBqBwY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iOTFlabqq4NBqBwY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-iOTFlabqq4NBqBwY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iOTFlabqq4NBqBwY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iOTFlabqq4NBqBwY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-iOTFlabqq4NBqBwY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iOTFlabqq4NBqBwY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iOTFlabqq4NBqBwY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iOTFlabqq4NBqBwY .marker.cross{stroke:#333333;}#mermaid-svg-iOTFlabqq4NBqBwY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iOTFlabqq4NBqBwY p{margin:0;}#mermaid-svg-iOTFlabqq4NBqBwY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-iOTFlabqq4NBqBwY .cluster-label text{fill:#333;}#mermaid-svg-iOTFlabqq4NBqBwY .cluster-label span{color:#333;}#mermaid-svg-iOTFlabqq4NBqBwY .cluster-label span p{background-color:transparent;}#mermaid-svg-iOTFlabqq4NBqBwY .label text,#mermaid-svg-iOTFlabqq4NBqBwY span{fill:#333;color:#333;}#mermaid-svg-iOTFlabqq4NBqBwY .node rect,#mermaid-svg-iOTFlabqq4NBqBwY .node circle,#mermaid-svg-iOTFlabqq4NBqBwY .node ellipse,#mermaid-svg-iOTFlabqq4NBqBwY .node polygon,#mermaid-svg-iOTFlabqq4NBqBwY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-iOTFlabqq4NBqBwY .rough-node .label text,#mermaid-svg-iOTFlabqq4NBqBwY .node .label text,#mermaid-svg-iOTFlabqq4NBqBwY .image-shape .label,#mermaid-svg-iOTFlabqq4NBqBwY .icon-shape .label{text-anchor:middle;}#mermaid-svg-iOTFlabqq4NBqBwY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-iOTFlabqq4NBqBwY .rough-node .label,#mermaid-svg-iOTFlabqq4NBqBwY .node .label,#mermaid-svg-iOTFlabqq4NBqBwY .image-shape .label,#mermaid-svg-iOTFlabqq4NBqBwY .icon-shape .label{text-align:center;}#mermaid-svg-iOTFlabqq4NBqBwY .node.clickable{cursor:pointer;}#mermaid-svg-iOTFlabqq4NBqBwY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-iOTFlabqq4NBqBwY .arrowheadPath{fill:#333333;}#mermaid-svg-iOTFlabqq4NBqBwY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-iOTFlabqq4NBqBwY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-iOTFlabqq4NBqBwY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iOTFlabqq4NBqBwY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-iOTFlabqq4NBqBwY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iOTFlabqq4NBqBwY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-iOTFlabqq4NBqBwY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-iOTFlabqq4NBqBwY .cluster text{fill:#333;}#mermaid-svg-iOTFlabqq4NBqBwY .cluster span{color:#333;}#mermaid-svg-iOTFlabqq4NBqBwY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-iOTFlabqq4NBqBwY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-iOTFlabqq4NBqBwY rect.text{fill:none;stroke-width:0;}#mermaid-svg-iOTFlabqq4NBqBwY .icon-shape,#mermaid-svg-iOTFlabqq4NBqBwY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iOTFlabqq4NBqBwY .icon-shape p,#mermaid-svg-iOTFlabqq4NBqBwY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-iOTFlabqq4NBqBwY .icon-shape .label rect,#mermaid-svg-iOTFlabqq4NBqBwY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iOTFlabqq4NBqBwY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-iOTFlabqq4NBqBwY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-iOTFlabqq4NBqBwY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 路径字符串
dentry 查找
得到 inode
权限检查
分配 struct file
填入进程 fd 表
返回 fd

步骤 说明
路径解析 沿 /hometest.txt 查 dentry;openat 可相对目录 fd
权限检查 inode 模式与进程 cred(含 CAP)
创建 struct file f_pos=0、打开标志、指向 file_operations
分配 FD 通常取当前进程最小未用 fd

4.2 常用 open 标志

标志 作用
O_RDONLY / O_WRONLY / O_RDWR 访问模式
O_CREAT 不存在则创建;modeumask 影响:mode & ~umask
O_TRUNC 写打开时截断为 0 长度
O_APPEND 写时偏移固定在文件尾
O_NONBLOCK 非阻塞(设备/socket 更常见)
O_SYNC / O_DSYNC 打开后每次 write 附带更强刷盘语义(见 [6.2](#标志 作用 O_RDONLY / O_WRONLY / O_RDWR 访问模式 O_CREAT 不存在则创建;mode 受 umask 影响:mode & ~umask O_TRUNC 写打开时截断为 0 长度 O_APPEND 写时偏移固定在文件尾 O_NONBLOCK 非阻塞(设备/socket 更常见) O_SYNC / O_DSYNC 打开后每次 write 附带更强刷盘语义(见 6.2) O_DIRECT 尽量绕过 Page Cache,需对齐(见 13) O_CLOEXEC exec 时自动关闭,防 fd 泄露))
O_DIRECT 尽量绕过 Page Cache,需对齐(见 [13](#标志 作用 O_RDONLY / O_WRONLY / O_RDWR 访问模式 O_CREAT 不存在则创建;mode 受 umask 影响:mode & ~umask O_TRUNC 写打开时截断为 0 长度 O_APPEND 写时偏移固定在文件尾 O_NONBLOCK 非阻塞(设备/socket 更常见) O_SYNC / O_DSYNC 打开后每次 write 附带更强刷盘语义(见 6.2) O_DIRECT 尽量绕过 Page Cache,需对齐(见 13) O_CLOEXEC exec 时自动关闭,防 fd 泄露))
O_CLOEXEC exec 时自动关闭,防 fd 泄露

fcntl(F_SETFL) 可在不重新 open 的情况下调整 O_NONBLOCKO_APPEND 等。

4.3 strace 示例

bash 复制代码
strace -e openat,read,write,close cat /etc/hostname 2>&1 | head -20

典型片段(路径因系统而异):

text 复制代码
openat(AT_FDCWD, "/etc/hostname", O_RDONLY) = 3
read(3, "myhost\n", 131072)             = 7
write(1, "myhost\n", 7)                 = 7
close(3)                                = 0

cat 从 fd=3 读文件,再 write 到 fd=1(stdout);可见 FD 与「打开对象」一一对应


5. read:Page Cache 与两次拷贝

c 复制代码
ssize_t n = read(fd, buf, len);

5.1 典型数据路径

常见误解:磁盘 → 用户 buf。实际是:

text 复制代码
  ┌──────────┐     缺页/未命中      ┌─────────────┐    copy_to_user    ┌──────────┐
  │ 磁盘/SSD │ ──────────────────► │ Page Cache  │ ─────────────────► │ 用户 buf │
  └──────────┘                     └─────────────┘                    └──────────┘
                                        ▲
                                   命中则跳过读盘

#mermaid-svg-ix0iyUdHYfdkMowW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ix0iyUdHYfdkMowW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ix0iyUdHYfdkMowW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ix0iyUdHYfdkMowW .error-icon{fill:#552222;}#mermaid-svg-ix0iyUdHYfdkMowW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ix0iyUdHYfdkMowW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ix0iyUdHYfdkMowW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ix0iyUdHYfdkMowW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ix0iyUdHYfdkMowW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ix0iyUdHYfdkMowW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ix0iyUdHYfdkMowW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ix0iyUdHYfdkMowW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ix0iyUdHYfdkMowW .marker.cross{stroke:#333333;}#mermaid-svg-ix0iyUdHYfdkMowW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ix0iyUdHYfdkMowW p{margin:0;}#mermaid-svg-ix0iyUdHYfdkMowW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ix0iyUdHYfdkMowW .cluster-label text{fill:#333;}#mermaid-svg-ix0iyUdHYfdkMowW .cluster-label span{color:#333;}#mermaid-svg-ix0iyUdHYfdkMowW .cluster-label span p{background-color:transparent;}#mermaid-svg-ix0iyUdHYfdkMowW .label text,#mermaid-svg-ix0iyUdHYfdkMowW span{fill:#333;color:#333;}#mermaid-svg-ix0iyUdHYfdkMowW .node rect,#mermaid-svg-ix0iyUdHYfdkMowW .node circle,#mermaid-svg-ix0iyUdHYfdkMowW .node ellipse,#mermaid-svg-ix0iyUdHYfdkMowW .node polygon,#mermaid-svg-ix0iyUdHYfdkMowW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ix0iyUdHYfdkMowW .rough-node .label text,#mermaid-svg-ix0iyUdHYfdkMowW .node .label text,#mermaid-svg-ix0iyUdHYfdkMowW .image-shape .label,#mermaid-svg-ix0iyUdHYfdkMowW .icon-shape .label{text-anchor:middle;}#mermaid-svg-ix0iyUdHYfdkMowW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ix0iyUdHYfdkMowW .rough-node .label,#mermaid-svg-ix0iyUdHYfdkMowW .node .label,#mermaid-svg-ix0iyUdHYfdkMowW .image-shape .label,#mermaid-svg-ix0iyUdHYfdkMowW .icon-shape .label{text-align:center;}#mermaid-svg-ix0iyUdHYfdkMowW .node.clickable{cursor:pointer;}#mermaid-svg-ix0iyUdHYfdkMowW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ix0iyUdHYfdkMowW .arrowheadPath{fill:#333333;}#mermaid-svg-ix0iyUdHYfdkMowW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ix0iyUdHYfdkMowW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ix0iyUdHYfdkMowW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ix0iyUdHYfdkMowW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ix0iyUdHYfdkMowW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ix0iyUdHYfdkMowW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ix0iyUdHYfdkMowW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ix0iyUdHYfdkMowW .cluster text{fill:#333;}#mermaid-svg-ix0iyUdHYfdkMowW .cluster span{color:#333;}#mermaid-svg-ix0iyUdHYfdkMowW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ix0iyUdHYfdkMowW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ix0iyUdHYfdkMowW rect.text{fill:none;stroke-width:0;}#mermaid-svg-ix0iyUdHYfdkMowW .icon-shape,#mermaid-svg-ix0iyUdHYfdkMowW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ix0iyUdHYfdkMowW .icon-shape p,#mermaid-svg-ix0iyUdHYfdkMowW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ix0iyUdHYfdkMowW .icon-shape .label rect,#mermaid-svg-ix0iyUdHYfdkMowW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ix0iyUdHYfdkMowW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ix0iyUdHYfdkMowW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ix0iyUdHYfdkMowW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 内核
用户态
read 系统调用
命中
未命中
顺序读时提前拉页
应用缓冲区 buf
Page Cache

按页 通常 4KB
readahead 预读
块设备

现象 原因
第二次读同一文件更快 Page Cache 命中
服务刚启动偏慢 冷缓存,大量缺页读盘
多进程读同一文件 可共享缓存页
read 返回 0 已到文件尾(EOF)
read 返回 -1 errno(如 EINTR 可重试)

5.2 pread / pwrite

API 特点
read/write 共享 struct file当前偏移
pread/pwrite 带偏移参数,不改变 f_pos,多线程读同一 fd 更安全

5.3 顺序读与 readahead

内核发现顺序访问 时,可用 readahead 提前把后续页调入 Page Cache,使应用下一次 read 更易命中。随机小 IO 则难以受益------这也是「拷贝一个大文件」往往比「海量小文件」更快的原因之一。


6. write、脏页与持久化

c 复制代码
ssize_t n = write(fd, buf, len);

6.1 写路径

#mermaid-svg-uxVlfWTnfUlhOkVW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uxVlfWTnfUlhOkVW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uxVlfWTnfUlhOkVW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uxVlfWTnfUlhOkVW .error-icon{fill:#552222;}#mermaid-svg-uxVlfWTnfUlhOkVW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uxVlfWTnfUlhOkVW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uxVlfWTnfUlhOkVW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uxVlfWTnfUlhOkVW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uxVlfWTnfUlhOkVW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uxVlfWTnfUlhOkVW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uxVlfWTnfUlhOkVW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uxVlfWTnfUlhOkVW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uxVlfWTnfUlhOkVW .marker.cross{stroke:#333333;}#mermaid-svg-uxVlfWTnfUlhOkVW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uxVlfWTnfUlhOkVW p{margin:0;}#mermaid-svg-uxVlfWTnfUlhOkVW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-uxVlfWTnfUlhOkVW .cluster-label text{fill:#333;}#mermaid-svg-uxVlfWTnfUlhOkVW .cluster-label span{color:#333;}#mermaid-svg-uxVlfWTnfUlhOkVW .cluster-label span p{background-color:transparent;}#mermaid-svg-uxVlfWTnfUlhOkVW .label text,#mermaid-svg-uxVlfWTnfUlhOkVW span{fill:#333;color:#333;}#mermaid-svg-uxVlfWTnfUlhOkVW .node rect,#mermaid-svg-uxVlfWTnfUlhOkVW .node circle,#mermaid-svg-uxVlfWTnfUlhOkVW .node ellipse,#mermaid-svg-uxVlfWTnfUlhOkVW .node polygon,#mermaid-svg-uxVlfWTnfUlhOkVW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uxVlfWTnfUlhOkVW .rough-node .label text,#mermaid-svg-uxVlfWTnfUlhOkVW .node .label text,#mermaid-svg-uxVlfWTnfUlhOkVW .image-shape .label,#mermaid-svg-uxVlfWTnfUlhOkVW .icon-shape .label{text-anchor:middle;}#mermaid-svg-uxVlfWTnfUlhOkVW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uxVlfWTnfUlhOkVW .rough-node .label,#mermaid-svg-uxVlfWTnfUlhOkVW .node .label,#mermaid-svg-uxVlfWTnfUlhOkVW .image-shape .label,#mermaid-svg-uxVlfWTnfUlhOkVW .icon-shape .label{text-align:center;}#mermaid-svg-uxVlfWTnfUlhOkVW .node.clickable{cursor:pointer;}#mermaid-svg-uxVlfWTnfUlhOkVW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uxVlfWTnfUlhOkVW .arrowheadPath{fill:#333333;}#mermaid-svg-uxVlfWTnfUlhOkVW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uxVlfWTnfUlhOkVW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uxVlfWTnfUlhOkVW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uxVlfWTnfUlhOkVW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uxVlfWTnfUlhOkVW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uxVlfWTnfUlhOkVW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uxVlfWTnfUlhOkVW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uxVlfWTnfUlhOkVW .cluster text{fill:#333;}#mermaid-svg-uxVlfWTnfUlhOkVW .cluster span{color:#333;}#mermaid-svg-uxVlfWTnfUlhOkVW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-uxVlfWTnfUlhOkVW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uxVlfWTnfUlhOkVW rect.text{fill:none;stroke-width:0;}#mermaid-svg-uxVlfWTnfUlhOkVW .icon-shape,#mermaid-svg-uxVlfWTnfUlhOkVW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uxVlfWTnfUlhOkVW .icon-shape p,#mermaid-svg-uxVlfWTnfUlhOkVW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uxVlfWTnfUlhOkVW .icon-shape .label rect,#mermaid-svg-uxVlfWTnfUlhOkVW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uxVlfWTnfUlhOkVW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uxVlfWTnfUlhOkVW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uxVlfWTnfUlhOkVW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} copy_from_user
标记 dirty
writeback 线程
用户 buf
Page Cache
脏页
磁盘

步骤 说明
数据进 Page Cache copy_from_user
标记脏页 该页需回写
write 返回 多表示数据已在缓存,不保证落盘
writeback kworker 等按水位、周期异步刷盘

6.2 持久化级别对照

从弱到强(概念上,具体还依赖文件系统与硬件写缓存):

级别 手段 应用层含义
① 默认 write 数据在 Page Cache,断电可能丢
fflush(stdio) C 库缓冲刷向内核 仍可能在 Page Cache,未必落盘
fdatasync 文件数据 元数据可能稍后(如大小)
fsync 数据 + 元数据 数据库日志常用
open(..., O_DSYNC) 每次写等价于带数据同步语义 性能开销大
open(..., O_SYNC) 更强同步语义 比 ⑤ 更严(含更多元数据同步)
text 复制代码
  应用 fwrite ──► stdio 缓冲 ──► write() ──► Page Cache ──► 磁盘
                  fflush           fsync/O_SYNC
                  ②                ③④⑤⑥

close 不能替代 fsync:关闭 fd 只释放打开实例;脏页仍可能尚未 writeback。

6.3 观察脏页与 writeback(运维)

bash 复制代码
cat /proc/meminfo | egrep 'Dirty|Writeback'
cat /proc/sys/vm/dirty_ratio
cat /proc/sys/vm/dirty_background_ratio
参数 含义(简化)
dirty_ratio 脏页占内存比例超阈值时,进程写可能被阻塞并推动回写
dirty_background_ratio 后台 writeback 启动阈值

大文件顺序写、数据库 checkpoint、虚拟机镜像合并都会推高 Dirty ;排障时结合 iostat -x 看磁盘 Util。


7. close 与 FD 泄露

close(fd) 释放进程 fd 表项;struct file 引用计数为 0 时回收。未刷盘脏页由全局 writeback 处理,与 close 无强同步关系

问题 后果 预防
长期不 close EMFILEopen 失败 RAII、O_CLOEXEC、泄漏检测
fork 后未关无用 fd 子进程继承,epoll 误触发 FD_CLOEXEC、显式关闭
异常路径未 close 同上 goto cleanup、defer 模式

本部分回顾(二 · 一次 IO 生命周期)open 不是「打开磁盘上的名字」,而是申请一条访问 inode 的通道read/write 在 Page Cache 里交接数据;write 返回 ≠ 落盘close 只收通道,不替你做 fsync。


8. 三层缓冲与数据路径

文件 IO 常叠加三层缓冲,排障「数据去哪了」须逐层分清:
#mermaid-svg-HwYQBeD0HnPdnCLD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HwYQBeD0HnPdnCLD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HwYQBeD0HnPdnCLD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HwYQBeD0HnPdnCLD .error-icon{fill:#552222;}#mermaid-svg-HwYQBeD0HnPdnCLD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HwYQBeD0HnPdnCLD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HwYQBeD0HnPdnCLD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HwYQBeD0HnPdnCLD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HwYQBeD0HnPdnCLD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HwYQBeD0HnPdnCLD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HwYQBeD0HnPdnCLD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HwYQBeD0HnPdnCLD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HwYQBeD0HnPdnCLD .marker.cross{stroke:#333333;}#mermaid-svg-HwYQBeD0HnPdnCLD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HwYQBeD0HnPdnCLD p{margin:0;}#mermaid-svg-HwYQBeD0HnPdnCLD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HwYQBeD0HnPdnCLD .cluster-label text{fill:#333;}#mermaid-svg-HwYQBeD0HnPdnCLD .cluster-label span{color:#333;}#mermaid-svg-HwYQBeD0HnPdnCLD .cluster-label span p{background-color:transparent;}#mermaid-svg-HwYQBeD0HnPdnCLD .label text,#mermaid-svg-HwYQBeD0HnPdnCLD span{fill:#333;color:#333;}#mermaid-svg-HwYQBeD0HnPdnCLD .node rect,#mermaid-svg-HwYQBeD0HnPdnCLD .node circle,#mermaid-svg-HwYQBeD0HnPdnCLD .node ellipse,#mermaid-svg-HwYQBeD0HnPdnCLD .node polygon,#mermaid-svg-HwYQBeD0HnPdnCLD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HwYQBeD0HnPdnCLD .rough-node .label text,#mermaid-svg-HwYQBeD0HnPdnCLD .node .label text,#mermaid-svg-HwYQBeD0HnPdnCLD .image-shape .label,#mermaid-svg-HwYQBeD0HnPdnCLD .icon-shape .label{text-anchor:middle;}#mermaid-svg-HwYQBeD0HnPdnCLD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HwYQBeD0HnPdnCLD .rough-node .label,#mermaid-svg-HwYQBeD0HnPdnCLD .node .label,#mermaid-svg-HwYQBeD0HnPdnCLD .image-shape .label,#mermaid-svg-HwYQBeD0HnPdnCLD .icon-shape .label{text-align:center;}#mermaid-svg-HwYQBeD0HnPdnCLD .node.clickable{cursor:pointer;}#mermaid-svg-HwYQBeD0HnPdnCLD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HwYQBeD0HnPdnCLD .arrowheadPath{fill:#333333;}#mermaid-svg-HwYQBeD0HnPdnCLD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HwYQBeD0HnPdnCLD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HwYQBeD0HnPdnCLD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HwYQBeD0HnPdnCLD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HwYQBeD0HnPdnCLD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HwYQBeD0HnPdnCLD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HwYQBeD0HnPdnCLD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HwYQBeD0HnPdnCLD .cluster text{fill:#333;}#mermaid-svg-HwYQBeD0HnPdnCLD .cluster span{color:#333;}#mermaid-svg-HwYQBeD0HnPdnCLD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HwYQBeD0HnPdnCLD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HwYQBeD0HnPdnCLD rect.text{fill:none;stroke-width:0;}#mermaid-svg-HwYQBeD0HnPdnCLD .icon-shape,#mermaid-svg-HwYQBeD0HnPdnCLD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HwYQBeD0HnPdnCLD .icon-shape p,#mermaid-svg-HwYQBeD0HnPdnCLD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HwYQBeD0HnPdnCLD .icon-shape .label rect,#mermaid-svg-HwYQBeD0HnPdnCLD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HwYQBeD0HnPdnCLD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HwYQBeD0HnPdnCLD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HwYQBeD0HnPdnCLD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 第 3 层:块设备
第 2 层:内核 Page Cache
第 1 层:应用 / stdio
write/read syscall
fopen 缓冲区

setvbuf 全缓冲/行缓冲
页缓存 4KB

LRU 淘汰 / readahead
块层队列

设备驱动
fprintf / fread
磁盘

层次 位置 优化目标 典型 API
stdio libc 减少 syscall 次数 setvbuffflush
Page Cache 内核 减少 磁盘 IO 自动;posix_fadvise 提示
块层 内核 合并、调度 IO 对应用透明

8.1 stdio 与 syscall

c 复制代码
/* 多次 fgetc 可能只触发少量 read */
FILE *fp = fopen("big.log", "r");
setvbuf(fp, buf, _IOFBF, 65536);  /* 64KB 全缓冲 */
缓冲模式 行为
_IONBF 无缓冲,每次 stdio 操作易触发 syscall
_IOLBF 行缓冲,遇 \n 刷新(tty 默认)
_IOFBF 全缓冲,满块或 fflush 才 syscall

8.2 Page Cache 策略(概念)

  • LRU 变体:active / inactive 链表,冷页被淘汰。
  • 共享:多进程只读映射同一文件可共享物理页。
  • drop_caches :仅测试环境用于模拟冷启动,生产慎用

9. 系统调用路径

read() 在 libc 中往往是薄包装,最终 syscall 进入内核。

跳读提示 :理解「用户态 ↔ 内核态切换 + fd 查表」即可;寄存器级细节见 [附录 A](#附录 A)。

9.1 时序

Page Cache sys_read libc 用户态 Page Cache sys_read libc 用户态 #mermaid-svg-bo91476PUQcC0PDj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bo91476PUQcC0PDj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bo91476PUQcC0PDj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bo91476PUQcC0PDj .error-icon{fill:#552222;}#mermaid-svg-bo91476PUQcC0PDj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bo91476PUQcC0PDj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bo91476PUQcC0PDj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bo91476PUQcC0PDj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bo91476PUQcC0PDj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bo91476PUQcC0PDj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bo91476PUQcC0PDj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bo91476PUQcC0PDj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bo91476PUQcC0PDj .marker.cross{stroke:#333333;}#mermaid-svg-bo91476PUQcC0PDj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bo91476PUQcC0PDj p{margin:0;}#mermaid-svg-bo91476PUQcC0PDj .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-bo91476PUQcC0PDj text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-bo91476PUQcC0PDj .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-bo91476PUQcC0PDj .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-bo91476PUQcC0PDj .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-bo91476PUQcC0PDj .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-bo91476PUQcC0PDj #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-bo91476PUQcC0PDj .sequenceNumber{fill:white;}#mermaid-svg-bo91476PUQcC0PDj #sequencenumber{fill:#333;}#mermaid-svg-bo91476PUQcC0PDj #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-bo91476PUQcC0PDj .messageText{fill:#333;stroke:none;}#mermaid-svg-bo91476PUQcC0PDj .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-bo91476PUQcC0PDj .labelText,#mermaid-svg-bo91476PUQcC0PDj .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-bo91476PUQcC0PDj .loopText,#mermaid-svg-bo91476PUQcC0PDj .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-bo91476PUQcC0PDj .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-bo91476PUQcC0PDj .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-bo91476PUQcC0PDj .noteText,#mermaid-svg-bo91476PUQcC0PDj .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-bo91476PUQcC0PDj .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-bo91476PUQcC0PDj .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-bo91476PUQcC0PDj .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-bo91476PUQcC0PDj .actorPopupMenu{position:absolute;}#mermaid-svg-bo91476PUQcC0PDj .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-bo91476PUQcC0PDj .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-bo91476PUQcC0PDj .actor-man circle,#mermaid-svg-bo91476PUQcC0PDj line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-bo91476PUQcC0PDj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 特权级切换 + 保存上下文 read(fd, buf, n) syscall fd → struct file → inode 查页 / 缺页读盘 copy_to_user 返回值 / errno

9.2 性能含义

模式 特点
每次 read 1 字节 syscall 极多,CPU 耗在态切换
每次 read 64KB+ syscall 少,更易吃满 Page Cache
stdio 大缓冲 + 大块 read 工程上常用组合

核心调用:openatclosereadwritelseekpread/pwritefcntl

本部分回顾(三 · 缓存与内核)stdio 帮你少打 syscall,Page Cache 帮你少碰磁盘;真正贵的是态切换 + 拷贝,大块 IO 比碎小 read 划算得多。


10. 五种 IO 模型对照

在阻塞 read/write 之上,Linux 为高并发与吞吐演化出多种模型(网络 IO 用得最多,文件 IO 也常组合使用)。

10.1 总览表

模型 行为概要 典型缺点 常见场景
阻塞 IO 直到数据就绪并完成拷贝才返回 一连接一线程,扩展差 简单 CLI、低并发
非阻塞 IO 未就绪返回 EAGAIN/EWOULDBLOCK 自旋轮询浪费 CPU 多路复用的前置
IO 多路复用 select/poll/epoll 等就绪再读写 epoll 需理解 LT/ET Nginx、Redis
异步 IO(AIO) 提交后立刻返回,完成时通知 传统 Linux AIO 对普通文件支持有限 特定场景;见 io_uring
零拷贝 数据少经用户态 需内核/驱动支持 静态文件、sendfile

10.2 行为对比(示意图)

#mermaid-svg-tVYsU3y6RxYtXln6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tVYsU3y6RxYtXln6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tVYsU3y6RxYtXln6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tVYsU3y6RxYtXln6 .error-icon{fill:#552222;}#mermaid-svg-tVYsU3y6RxYtXln6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tVYsU3y6RxYtXln6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tVYsU3y6RxYtXln6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tVYsU3y6RxYtXln6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tVYsU3y6RxYtXln6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tVYsU3y6RxYtXln6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tVYsU3y6RxYtXln6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tVYsU3y6RxYtXln6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tVYsU3y6RxYtXln6 .marker.cross{stroke:#333333;}#mermaid-svg-tVYsU3y6RxYtXln6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tVYsU3y6RxYtXln6 p{margin:0;}#mermaid-svg-tVYsU3y6RxYtXln6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tVYsU3y6RxYtXln6 .cluster-label text{fill:#333;}#mermaid-svg-tVYsU3y6RxYtXln6 .cluster-label span{color:#333;}#mermaid-svg-tVYsU3y6RxYtXln6 .cluster-label span p{background-color:transparent;}#mermaid-svg-tVYsU3y6RxYtXln6 .label text,#mermaid-svg-tVYsU3y6RxYtXln6 span{fill:#333;color:#333;}#mermaid-svg-tVYsU3y6RxYtXln6 .node rect,#mermaid-svg-tVYsU3y6RxYtXln6 .node circle,#mermaid-svg-tVYsU3y6RxYtXln6 .node ellipse,#mermaid-svg-tVYsU3y6RxYtXln6 .node polygon,#mermaid-svg-tVYsU3y6RxYtXln6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tVYsU3y6RxYtXln6 .rough-node .label text,#mermaid-svg-tVYsU3y6RxYtXln6 .node .label text,#mermaid-svg-tVYsU3y6RxYtXln6 .image-shape .label,#mermaid-svg-tVYsU3y6RxYtXln6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-tVYsU3y6RxYtXln6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tVYsU3y6RxYtXln6 .rough-node .label,#mermaid-svg-tVYsU3y6RxYtXln6 .node .label,#mermaid-svg-tVYsU3y6RxYtXln6 .image-shape .label,#mermaid-svg-tVYsU3y6RxYtXln6 .icon-shape .label{text-align:center;}#mermaid-svg-tVYsU3y6RxYtXln6 .node.clickable{cursor:pointer;}#mermaid-svg-tVYsU3y6RxYtXln6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tVYsU3y6RxYtXln6 .arrowheadPath{fill:#333333;}#mermaid-svg-tVYsU3y6RxYtXln6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tVYsU3y6RxYtXln6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tVYsU3y6RxYtXln6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tVYsU3y6RxYtXln6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tVYsU3y6RxYtXln6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tVYsU3y6RxYtXln6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tVYsU3y6RxYtXln6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tVYsU3y6RxYtXln6 .cluster text{fill:#333;}#mermaid-svg-tVYsU3y6RxYtXln6 .cluster span{color:#333;}#mermaid-svg-tVYsU3y6RxYtXln6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tVYsU3y6RxYtXln6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tVYsU3y6RxYtXln6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-tVYsU3y6RxYtXln6 .icon-shape,#mermaid-svg-tVYsU3y6RxYtXln6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tVYsU3y6RxYtXln6 .icon-shape p,#mermaid-svg-tVYsU3y6RxYtXln6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tVYsU3y6RxYtXln6 .icon-shape .label rect,#mermaid-svg-tVYsU3y6RxYtXln6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tVYsU3y6RxYtXln6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tVYsU3y6RxYtXln6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tVYsU3y6RxYtXln6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 多路复用 epoll
epoll_wait
就绪 fd 列表
read/write
非阻塞 IO


read
就绪?
EAGAIN 立即返回
拷贝
阻塞 IO
read
阻塞等待
数据拷贝到用户

text 复制代码
阻塞:      [应用] ──等待──> [内核就绪] ──> 返回
非阻塞:    [应用] <──EAGAIN── [内核]  (循环再问)
多路复用:  epoll_wait ──就绪列表──> 再 read/write
零拷贝:    数据尽量在内核路径转发,少 copy_to_user

epoll 细节见《select、poll、epoll 深入解析》。

10.3 io_uring:现代统一接口(简注)

传统 Linux AIO 对普通文件支持有限,网络高并发长期依赖 epoll + 非阻塞io_uring (Linux 5.1+,持续演进)通过提交队列 SQ / 完成队列 CQ ,把 read、write、accept、send 等收拢到一次 mmap 共享环 ,减少 syscall 次数,并可在内核侧串联 poll、链接操作、缓冲区注册,向「阻塞 / 非阻塞 / 多路复用 / 部分零拷贝」收敛。

对比 epoll 时代 io_uring
就绪通知 epoll_wait 后再 read/write 可在 CQ 直接收完成事件
syscall 频率 通常每个操作至少一次 批量提交,可极低
学习成本 成熟、资料多 概念多,需内核版本与库支持

生产上 Nginx、Redis、传统后端仍以 epoll 为主;新项目、存储与框架(如部分数据库、代理)越来越多地评估 io_uring。深入见《Linux io_uring机制详解》。


11. read 与 mmap、零拷贝

11.1 read vs mmap

维度 read/write mmapMAP_SHARED/PRIVATE
数据路径 显式拷贝到用户 buf 映射页,访问时缺页填入/共享 Page Cache
适用 流式、小块随机、简单逻辑 大文件随机访问、多进程共享只读映射
注意 缓冲区自己管理 文件变短可能导致 SIGBUS ;需 munmap

两者底层仍常共用 Page Cachemmap 不是「绕过缓存」,而是减少一次显式用户态拷贝

11.2 sendfile 零拷贝(概念)

静态文件 HTTP 发送典型路径:

text 复制代码
  传统:  磁盘 → 内核缓存 → 用户 buf → 内核 socket 缓冲 → 网卡
  sendfile: 磁盘 → 内核缓存 ──────────────► socket 缓冲 → 网卡
                      (少一次 copy_to_user / copy_from_user)

#mermaid-svg-bHG0pdxzlNzJFUen{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bHG0pdxzlNzJFUen .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bHG0pdxzlNzJFUen .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bHG0pdxzlNzJFUen .error-icon{fill:#552222;}#mermaid-svg-bHG0pdxzlNzJFUen .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bHG0pdxzlNzJFUen .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bHG0pdxzlNzJFUen .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bHG0pdxzlNzJFUen .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bHG0pdxzlNzJFUen .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bHG0pdxzlNzJFUen .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bHG0pdxzlNzJFUen .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bHG0pdxzlNzJFUen .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bHG0pdxzlNzJFUen .marker.cross{stroke:#333333;}#mermaid-svg-bHG0pdxzlNzJFUen svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bHG0pdxzlNzJFUen p{margin:0;}#mermaid-svg-bHG0pdxzlNzJFUen .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bHG0pdxzlNzJFUen .cluster-label text{fill:#333;}#mermaid-svg-bHG0pdxzlNzJFUen .cluster-label span{color:#333;}#mermaid-svg-bHG0pdxzlNzJFUen .cluster-label span p{background-color:transparent;}#mermaid-svg-bHG0pdxzlNzJFUen .label text,#mermaid-svg-bHG0pdxzlNzJFUen span{fill:#333;color:#333;}#mermaid-svg-bHG0pdxzlNzJFUen .node rect,#mermaid-svg-bHG0pdxzlNzJFUen .node circle,#mermaid-svg-bHG0pdxzlNzJFUen .node ellipse,#mermaid-svg-bHG0pdxzlNzJFUen .node polygon,#mermaid-svg-bHG0pdxzlNzJFUen .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bHG0pdxzlNzJFUen .rough-node .label text,#mermaid-svg-bHG0pdxzlNzJFUen .node .label text,#mermaid-svg-bHG0pdxzlNzJFUen .image-shape .label,#mermaid-svg-bHG0pdxzlNzJFUen .icon-shape .label{text-anchor:middle;}#mermaid-svg-bHG0pdxzlNzJFUen .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bHG0pdxzlNzJFUen .rough-node .label,#mermaid-svg-bHG0pdxzlNzJFUen .node .label,#mermaid-svg-bHG0pdxzlNzJFUen .image-shape .label,#mermaid-svg-bHG0pdxzlNzJFUen .icon-shape .label{text-align:center;}#mermaid-svg-bHG0pdxzlNzJFUen .node.clickable{cursor:pointer;}#mermaid-svg-bHG0pdxzlNzJFUen .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bHG0pdxzlNzJFUen .arrowheadPath{fill:#333333;}#mermaid-svg-bHG0pdxzlNzJFUen .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bHG0pdxzlNzJFUen .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bHG0pdxzlNzJFUen .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bHG0pdxzlNzJFUen .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bHG0pdxzlNzJFUen .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bHG0pdxzlNzJFUen .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bHG0pdxzlNzJFUen .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bHG0pdxzlNzJFUen .cluster text{fill:#333;}#mermaid-svg-bHG0pdxzlNzJFUen .cluster span{color:#333;}#mermaid-svg-bHG0pdxzlNzJFUen div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bHG0pdxzlNzJFUen .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bHG0pdxzlNzJFUen rect.text{fill:none;stroke-width:0;}#mermaid-svg-bHG0pdxzlNzJFUen .icon-shape,#mermaid-svg-bHG0pdxzlNzJFUen .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bHG0pdxzlNzJFUen .icon-shape p,#mermaid-svg-bHG0pdxzlNzJFUen .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bHG0pdxzlNzJFUen .icon-shape .label rect,#mermaid-svg-bHG0pdxzlNzJFUen .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bHG0pdxzlNzJFUen .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bHG0pdxzlNzJFUen .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bHG0pdxzlNzJFUen :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} sendfile
文件
Page Cache
socket 缓冲区
网卡

相关 API:sendfilesplicetee;与用户态 mmap+write 组合相比,减少 CPU 拷贝次数,高 QPS 下载、Kafka 等场景常见。

本部分回顾(四 · 模型与延伸) :单线程阻塞 IO 够用就别复杂化;高并发网络 用多路复用;少拷贝 用 sendfile/mmap;下一代批量 IO 可关注 io_uring------但 Page Cache 与持久化语义仍是底座。


12. 常见误区

误区 事实
write 成功 = 已落盘 多数情况只进 Page Cache;要 fsync / O_SYNC
close = 刷盘 close 释放 fd;脏页由异步 writeback 处理
硬链接 = 复制文件 同 inode,不复制数据块
软链接删了 = 删原文件 只删链接 inode;原文件不受影响
mmap 一定比 read 小文件、顺序一次性读,可能差不多甚至更差
Page Cache 浪费内存 空闲内存用作缓存,压力上来会被回收
所有「文件」都走磁盘缓存 socket、pipe 等走各自缓冲区,非 Page Cache

13. 性能调优要点

目标 建议
减少 syscall stdio/应用层大缓冲;批量 read/write
提高顺序读吞吐 大块读;依赖内核 readahead
降低延迟抖动 注意 dirty_ratio 导致写阻塞;数据库用 O_DIRECT 自管缓冲
高并发连接 文件 IO 少阻塞;网络用 epoll + 非阻塞
发送静态文件 sendfile / 反向代理零拷贝能力
多线程同一 fd pread/pwrite 或每线程独立 open
容器/NFS 在真实存储上压测;Overlay/NFS 缓存行为与本地 ext4 不同

O_DIRECT 注意:缓冲区、文件偏移、长度常需与块大小对齐;绕过 Page Cache 后应用须自己处理缓存一致性。


14. 速查与验证

14.1 命令速查

目的 命令
跟踪文件 syscall strace -ff -e trace=file -p <pid>
查看进程 fd ls -l /proc/<pid>/fd
inode / 元数据 statls -li
内存与缓存 free -h;`grep -E 'Cached
IO 统计 iostat -x 1
测试冷缓存(仅测试机) sync; echo 3 > /proc/sys/vm/drop_caches
强制刷盘 fsync(fd) / sync(整机,重)

14.2 验证 Page Cache

bash 复制代码
FILE=/path/to/largefile
/usr/bin/time -f '%e sec' cat "$FILE" > /dev/null    # 第 1 次
/usr/bin/time -f '%e sec' cat "$FILE" > /dev/null    # 第 2 次(通常更快)

14.3 验证「写未落盘」(实验环境)

bash 复制代码
# 终端 A:写入后不 fsync
dd if=/dev/zero of=/tmp/test.img bs=1M count=100 conv=fdatasync
# 对比:去掉 conv=fdatasync,断电或 kill 前数据可能只在缓存

14.4 总览速查卡

text 复制代码
┌────────────────────────────────────────────────────────────┐
│ 对象:  路径 → dentry → inode ← struct file ← fd           │
│ 读:    磁盘 ⇄ Page Cache ─copy_to_user→ 用户 buf           │
│ 写:    用户 buf → Page Cache(脏页) → writeback → 磁盘      │
│ 持久:  write < fflush < fdatasync < fsync < O_SYNC         │
│ 性能:  大块 IO + 少 syscall + epoll/sendfile(按场景)     │
└────────────────────────────────────────────────────────────┘

附录 A:x86_64 系统调用传参示意

仅供对照「syscall 如何带参数」,与日常文件 IO 排障无强依赖,可跳过。

read(fd, buf, count) 为例(Linux x86_64 ABI):

寄存器 含义(read 示例)
rax 系统调用号
rdi fd
rsi buf 指针
rdx count

返回:rax 为返回值;错误时内核返回负值,libc 转为 errno


落地注意

  • 容器 OverlayFSNFSCeph 等下 Page Cache 与一致性语义与本地 ext4 不同,压测须在目标环境复现。
  • 调试「丢数据」分层排查:stdio 未 fflushwrite 未 fsync应用队列未刷磁盘写缓存未掉电保护
  • 网络 fd普通文件 fd 共用 epoll,但数据路径不同,勿用文件 IO 经验直接套 socket 阻塞行为。
  • 版本与挂载选项(noatimebarrier、FS 特性)以当前内核与 man 2 open 为准。

延伸阅读(仓库内)

主题 文档
FD 机制、/proc/fd、上限 Linux文件描述符FD机制深度解析.md
epoll / select / poll select_poll_epoll详解.md
io_uring Linux_io_uring机制详解_异步IO原理SQPOLL与性能调优实践.md
OverlayFS 与容器 Linux_OverlayFS详解.md
共享内存 vs 文件映射 Linux共享内存机制详解_映射原理API实战与同步回收避坑.md
proc/sysfs 虚拟 FS Linux_proc_sysfs与devfs_虚拟文件系统解析.md

一句话 :Linux 文件 IO = inode 表身份 + FD 表打开方式 ,数据经 Page Cache 中转;持久化与性能要在 stdio / syscall / fsync / IO 模型 四层分别做决策。

相关推荐
为思念酝酿的痛1 小时前
线程同步与互斥
linux·运维·服务器·后端
一条代码鱼2 小时前
Linux 文件实时同步完全指南:Lsyncd vs Inotifywait+Rsync
linux·运维·服务器
艾莉丝努力练剑2 小时前
【Linux网络】Linux 网络编程:传输层协议TCP(三)
linux·运维·服务器·网络·tcp/ip·http
小程故事多_802 小时前
从想法到落地零返工,AI Agent六阶段自动化开发全流水线实践
运维·人工智能·自动化
keyipatience2 小时前
21,22 (半)深入理解Linux重定向与缓冲区机制
linux·运维·服务器
风向决定发型丶2 小时前
Logrotate配置nginx日志切割
运维·nginx
fengyehongWorld2 小时前
Linux command 命令
linux
yyuuuzz2 小时前
aws亚马逊云上运维常见问题梳理
运维·服务器·网络·云计算·aws
2201_761199042 小时前
python运维1
运维·开发语言·python