写文件的时候,数据到底是怎么跑到磁盘上的?很多人会说「调用 write 就行」,但 Linux 实际上给你留了三条路:DAX、Direct I/O、Normal I/O。它们各走各的,延迟差着好几个数量级。
应用程序:发起 IO 请求
不管你用哪种方式写文件,代码都一样简单:
// 普通写
write(fd, buf, 4096);
// Direct I/O
open("/path/to/file", O_DIRECT);
write(fd, buf, 4096);
// DAX(需要文件系统支持)
mmap(file, PROT_READ|PROT_WRITE, MAP_SHARED);
buf[0] = 'a'; // 直接写持久内存
区别不在应用层代码,在于内核怎么「接」这个请求。
三条路径,差在哪
应用程序的 IO 请求进入内核后,会被文件系统层「分流」:
DAX 路径:文件系统直接建立物理内存到持久内存的映射,绕过 Page Cache,也绕过块 IO 层。数据直接落在 PMEM 设备上,延迟 ~100ns。
Direct I/O 路径:带上 O_DIRECT 标志后,文件系统跳过 Page Cache,直接把请求送到通用块 IO 栈,落盘。延迟 ~μs 级别。(注:生产注意:实际使用 O_DIRECT 时,buffer 起始地址、文件偏移量、写入长度,都必须按 512B 或 4KB 对齐,否则 write 会直接报 EINVAL 错误。)
Normal I/O 路径:默认方式,数据先写入 Page Cache。普通 write 调用瞬间返回(μs 级),真正由后台线程 writeback 落盘耗时 ms 级,读缓存命中时则为 ~100ns。
简单说:DAX 零拷贝,Direct I/O 自己管缓存,Normal I/O 让内核管缓存。
什么时候选哪条
DAX 需要文件系统支持(ext4/XFS 都可以),且文件所在设备必须是持久内存(PMEM)。典型场景:内存数据库(Redis 持久化)、游戏服务器、量化交易系统。
Direct I/O 的典型用户是数据库。MySQL 和 PostgreSQL 有自己的 buffer pool,不需要 Linux 的 Page Cache 叠床架屋。用 O_DIRECT 跳过缓存,自己说了算。
普通业务用 Normal I/O 就够了。读热点文件会被 Page Cache 加速,写操作被缓存吸收避免阻塞应用。除非你测出来 Page Cache 反而拖后腿,否则不需要折腾 Direct I/O。
怎么选
有 PMEM 硬件、用 DAX;数据库自己管缓存、用 Direct I/O;其他所有场景、用 Normal I/O。
性能数字记不住没关系,记住这个逻辑就行:绕过越多的层,延迟越低,但代价是放弃了内核帮你做的那些事。缓存管理、数据可靠性、顺序优化——你不用,内核替你做;你想自己来,就得多操心。
关注「悉数洞鉴」,看更多 Linux 内核图解系列。