浅谈virtio-pmem
文章目录
之前介绍了DAMON 与 virtio-balloon:先回收 Guest 中长期不用的文件缓存,再让 Host 利用这些内存。
本文换一个角度:能不能一开始就少保留一份缓存?
下面通过多个虚拟机读取同一份只读镜像的例子,浅谈 virtio-pmem。本文讨论 virtio-pmem 配合DAX的文件访问方式。
What
先把最容易混淆的一点说清楚:
1 | 使用 virtio-pmem |
virtio-pmem 是一种半虚拟化的持久内存设备。 QEMU 可以使用普通存储上的文件作为它的后端,不要求 Host 配备真正的 NVDIMM。
它向 Guest 提供一段可以直接访问的内存区域,并通过单独的 Flush(刷新)请求,实现写入的持久化。
配合 DAX(Direct Access,直接访问),Guest 可以绕过自己的 Page Cache,直接使用 Host 后端文件对应的页面。
所以:
不是让文件数据“不再缓存”,而是不必在 Guest 中再缓存一份。
Why
先看 Guest 和 Host 都使用页缓存的传统方案。
Host 可能缓存了一份镜像数据,Guest 读取其中的文件时,又在自己的 Page Cache 中保留一份。
于是,同一段数据可能同时存在于:
1 | Host Page Cache |
当多个 Guest 读取同一份镜像时,每个 Guest 还可能分别保留自己的副本。
这些 Guest 缓存,最终也需要占用 Host 的物理内存。
因此,virtio-pmem 配合 DAX 的目的可以概括为:
减少 Guest 与 Host 之间重复保存的文件数据缓存。
基础 Example
假设 Guest A 和 Guest B 都读取同一份只读后端镜像中的同一段文件数据。
下面只比较这部分文件数据缓存。
传统的双层缓存
1 | Host Page Cache |
Host 有一份,两个 Guest 又各有一份。
virtio-pmem + DAX
1 | Guest A Guest B |
两个 Guest 不再分别建立这部分文件数据的 Page Cache,而是通过映射使用同一份 Host 页面。
这里的前提是:它们使用的是同一个只读后端,而不只是两个文件的内容碰巧相同。
DSec 论文中的 virtio-pmem 内存优化,利用的就是这种减少重复缓存、共享 Host 页面的方法。
How
只看三个动作。
1. Host 提供文件对应的内存映射
Host 使用一个文件作为 virtio-pmem 的后端,把对应的映射提供给 Guest。
Guest 驱动获得这段区域的位置和大小,随后就可以访问它。
可以简单理解为:
给 Guest 一扇直接访问后端文件页面的“窗口”。
2. Guest 通过 DAX 绕过自己的文件数据缓存
在支持并启用 DAX 的文件访问路径中,Guest 不再先把数据复制到自己的 Page Cache,而是访问 virtio-pmem 提供的页面。
1 | 传统双层缓存: |
省掉的是 Guest 中间的那份文件数据缓存,不是所有内存开销。
3. 需要持久化写入时,单独发送 Flush
前面的例子只涉及读取,但 virtio-pmem 本身也支持写入。
需要区分:
1 | 数据已经写入映射页面 |
对于可写文件,Guest 应用通过 fsync() 或 msync() 等接口请求持久化,Guest 软件栈再通过 virtio-pmem 的 Flush 机制通知 Host。
1 | Guest 写入数据 |
规范要求:Flush 成功完成前,设备必须保证该请求之前已经完成的写入,能够在设备重置或掉电后保留。
因此,这里的 virtqueue 不是用来逐次搬运文件读写数据的,而是用来发送 Flush 请求。
DSec 中怎么使用?

DSec 将 virtio-pmem + DAX 用于只读的 EROFS 基础镜像和工具层,让同一 Host 上的多个 microVM 共享后端页面。
它并没有把所有磁盘都换成 virtio-pmem。论文(§5.2)指出,冷访问和页面管理元数据仍有开销;对于较大的可写磁盘,DSec 使用 DAMON 与 virtio-balloon 回收冷缓存。
两者可以这样区分:
1 | virtio-pmem + DAX: |
记忆图
1 | 数据访问: |
一句话概括:
文件数据通过映射访问,避免在 Guest 中重复缓存;需要持久化的写入,再通过 Flush 通知 Host 完成。
参考资料: