PMFS — 第一个为字节寻址持久内存设计的文件系统
是什么
PMFS(Persistent Memory File System)是 Intel Labs 2014 年发表的文件系统原型。它假设硬盘已经被一种”速度接近内存、断电不丢、按字节读写”的新存储取代,然后从零设计了一套与之配套的内核机制。
日常类比:传统文件系统像图书馆——书放仓库(磁盘),要看时管理员先搬到借阅台(page cache);PMFS 像直接把整个书架接到你桌上,伸手就能翻,不需要搬来搬去。
这篇论文当年是纯学术——硬件还不存在。5 年后(2019)Intel 发布 Optane DC 持久内存,PMFS 的设计直接成了 Linux 内核 ext4-DAX / xfs-DAX 的蓝本。
为什么重要
不理解 PMFS,下面这些事都没法解释:
- 为什么 Linux 有
ext4-DAX/xfs-DAX这种”绕过 page cache”的挂载选项 - 为什么 Optane(2019)发布后 Intel 直接说”软件栈早就准备好了”
- 为什么现代 CXL Type-3(带持久化的内存扩展卡)继续走”字节寻址 + DAX”路线
- 为什么 NOVA / Strata / SplitFS 这些持久内存文件系统的论文都引 PMFS
一句话:PMFS 是”存储慢→存储快”硬件革命前夜,软件侧的奠基论文。
核心要点
PMFS 解决的是一个根本问题:当存储和内存速度接近时,传统文件系统的所有假设都失效。
三大核心机制:
-
DAX(Direct Access)—— 绕过 page cache:应用
mmap文件后,CPU 的 load/store 指令直接打到持久内存。传统路径要先把磁盘数据复制到 page cache,再从 page cache 复制给应用——两次拷贝在内存级存储上是纯浪费。 -
CLFLUSH + 内存屏障保证持久化顺序:CPU cache 写到持久内存才算”真持久”。PMFS 用
CLFLUSH(强制 cache 行刷出)+SFENCE(内存屏障)控制写入顺序,不依赖fsync这种”全量同步”的重锤。 -
原子 8 字节写 + 日志保证元数据一致性:硬件保证 8 字节对齐写要么全成功要么全失败。元数据更新塞进 8 字节,跨边界的更新走小日志(undo log)。
实践案例
案例 1:传统文件系统 vs PMFS 写一个字节
传统 ext4 写 1 字节流程:
应用 write() → 内核 → page cache(DRAM 中暂存) → 应用返回 → 后台 flush → 块设备驱动 → 磁盘PMFS DAX 写 1 字节流程:
应用 mmap() 拿到指针 p*p = 'a' ← CPU store 直接打到持久内存CLFLUSH(p); SFENCE ← 强制 cache 刷出 + 等完成第二条路径少了 2 次拷贝、1 次系统调用、1 次后台 flush。延迟从微秒级降到几十纳秒。
案例 2:为什么需要 CLFLUSH
CPU 写内存其实是写到 cache,cache 什么时候刷到内存由 CPU 自己决定。对 DRAM 这无所谓——反正断电都丢。对持久内存就是大问题:
log_entry->valid = 1; // ← 这条可能还在 cachedata[0] = 0x42; // ← 这条已经刷到持久内存// 突然断电重启后 data[0] = 0x42 还在,但 log_entry->valid = 1 丢了——日志说”没写”,数据却已经写了,灾难。
PMFS 的纪律:每次需要持久化的写后面都跟一对 CLFLUSH + SFENCE,强制顺序。
案例 3:DAX 在 Linux 内核里的样子
PMFS 的设计后来直接进了主线内核。今天 Linux 上你可以:
# 把一块持久内存格式化为 ext4,开 DAXmkfs.ext4 /dev/pmem0mount -o dax /dev/pmem0 /mnt/pmem应用 mmap("/mnt/pmem/foo") 拿到的指针就是真实持久内存地址。这套接口的祖宗就是 PMFS。
踩过的坑
-
DAX 让应用承担一致性责任:绕过 page cache 后,应用必须自己管
CLFLUSH顺序。错一个就在断电时丢数据。这把”一致性”从内核推给了用户态——很多老应用根本写不对。 -
原子性只保证 8 字节对齐写:写一个 16 字节的结构体不是原子的。跨边界更新必须走日志或 copy-on-write,不能直接 store。新人很容易忽略。
-
硬件迟到 5 年:PMFS 2014 发表,Intel Optane 2019 才出货。论文用模拟器评测,真硬件出来后部分性能假设要修正(比如 Optane 的写延迟其实是 DRAM 的 3-5x,不是接近)。
-
Optane 已被砍:2022 年 Intel 宣布停产 Optane DC PMM。但”字节寻址持久存储”这个范式被 CXL.mem + 持久化接力走下去——PMFS 的思想没死,硬件载体换了。
适用 vs 不适用场景
适用:
- 持久内存(PMEM)/ CXL Type-3 持久化设备的文件系统设计
- 需要极低写延迟(< 1 微秒)的场景:金融日志、内存数据库 WAL
- 想理解 ext4-DAX / xfs-DAX 内部为什么这么设计
- 学习”硬件抽象重新设计”——什么时候该绕开旧抽象,什么时候该兼容
不适用:
- 传统块存储(NVMe SSD / SATA)—— page cache 还是必要的
- 大文件顺序写场景(视频、备份)—— DAX 没有优势,page cache 反而能预读
- 应用不愿改动一致性逻辑 —— DAX 的代价是把责任推给应用
- 需要跨机一致性(分布式存储)—— PMFS 只解决单机持久化
历史小故事(可跳过)
- 2009 年前后:Intel 内部开始研究 PCM(相变存储器)/ STT-RAM。硬件团队预判”未来 5-10 年存储和内存的边界会模糊”
- 2014 年:Dulloor 团队在 EuroSys 发表 PMFS。当时全场最大的争议是”硬件还没出,谈软件早不早”
- 2015-2018 年:Linux 内核合并 DAX 支持;ext4、xfs 都加了 DAX 选项;NOVA、Strata 等论文继续推进
- 2019 年:Intel Optane DC PMM 出货,PMFS 设计变成生产代码;Intel PMDK 库面向应用开发者发布
- 2022 年:Intel 宣布逐步停产 Optane,社区把目光转向 CXL.mem 持久化变体
- 2024 年至今:CXL Type-3 设备开始量产,PMFS 提出的”DAX + 字节寻址”再次成为主流路径
学到什么
- 硬件革命前的软件设计:好论文不一定要等硬件齐了才写——PMFS 提前 5 年布局,硬件一出就接住
- 抽象代价的重新分配:DAX 把一致性责任从内核推给应用,换来 10x 的延迟降低。没有银弹,只有取舍
- 范式比载体更耐久:Optane 死了,但”字节寻址持久存储 + DAX 文件系统”的思想跨硬件代际活下来
- CLFLUSH 是新的 fsync:在持久内存时代,单条缓存行的 flush 才是持久化的真单位,不再是块或文件
- 存储和内存的二分法正在消失:DRAM / SSD / HDD 三层金字塔被持久内存压成两层;这反过来动摇了 LSM、page cache、WAL 这些为”慢存储”设计的经典数据结构
延伸阅读
- 论文 12 页 PDF:Dulloor et al., EuroSys 2014
- Linux 内核 DAX 文档:
Documentation/filesystems/dax.txt(讲 ext4/xfs DAX 实现细节) - 跟进论文:NOVA(FAST 2016)—— 在 PMFS 思路上加 per-CPU 日志,并发性更好
- 跟进论文:Strata(SOSP 2017)—— 把持久内存、SSD、HDD 做成多层混合,用户态文件系统
- CXL 持久化展望:CXL 3.0 spec 中 Type-3 设备的持久化语义
- 工程视角:PMDK 项目主页 —— Intel 开源的持久内存编程库,封装 CLFLUSH/SFENCE 这些细节
- 反思视角:Optane 停产后 SNIA 社区关于”持久内存软件栈下一步”的讨论文档
关联
- nvm —— 非易失内存的硬件背景;PMFS 软件栈的服务对象
- nvme-protocol-2017 —— 块设备时代的协议优化;PMFS 走的是相反路径(绕过块层)
- aries-1992 —— 数据库恢复算法的经典;PMFS 的元数据日志思想可以追溯到 ARIES
- rocksdb-lsm —— LSM 树为慢存储设计;持久内存让”内存层 + 磁盘层”分层假设松动
- memcached-fb-2013 —— Facebook 大规模缓存实践;持久内存让缓存与存储边界变模糊
反向链接
- aries-1992 —— ARIES 1992 — 数据库崩溃后怎么把账目对回来
- esx-memory-2002 —— ESX Memory 2002 — 让一台机器假装比自己更大的四个魔术
- kim-rowhammer-2014 —— RowHammer — 只读内存也能把邻居的 bit 敲 flip
- memcached-fb-2013 —— Scaling Memcache at Facebook — 万台缓存怎么不被踩塌
- nvm —— nvm — 在同一台机器上轻松切换 Node 版本
- nvme-protocol-2017 —— NVMe — 为 SSD 重写的存储协议
- rocksdb-lsm —— LSM-tree 与 RocksDB — 把所有写都变成顺序写