之前介绍了DAMON 与 virtio-balloon:先回收 Guest 中长期不用的文件缓存,再让 Host 利用这些内存。

本文换一个角度:能不能一开始就少保留一份缓存?

下面通过多个虚拟机读取同一份只读镜像的例子,浅谈 virtio-pmem。本文讨论 virtio-pmem 配合DAX的文件访问方式。

What

先把最容易混淆的一点说清楚:

1
2
3
使用 virtio-pmem
≠
Host 必须安装真正的持久内存硬件

virtio-pmem 是一种半虚拟化的持久内存设备。 QEMU 可以使用普通存储上的文件作为它的后端,不要求 Host 配备真正的 NVDIMM。

它向 Guest 提供一段可以直接访问的内存区域,并通过单独的 Flush(刷新)请求,实现写入的持久化。

配合 DAX(Direct Access,直接访问),Guest 可以绕过自己的 Page Cache,直接使用 Host 后端文件对应的页面。

所以:

不是让文件数据“不再缓存”,而是不必在 Guest 中再缓存一份。

Why

先看 Guest 和 Host 都使用页缓存的传统方案。

Host 可能缓存了一份镜像数据,Guest 读取其中的文件时,又在自己的 Page Cache 中保留一份。

于是,同一段数据可能同时存在于:

1
2
3
Host Page Cache

Guest Page Cache

当多个 Guest 读取同一份镜像时,每个 Guest 还可能分别保留自己的副本。

这些 Guest 缓存,最终也需要占用 Host 的物理内存。

因此,virtio-pmem 配合 DAX 的目的可以概括为:

减少 Guest 与 Host 之间重复保存的文件数据缓存。

基础 Example

假设 Guest A 和 Guest B 都读取同一份只读后端镜像中的同一段文件数据。

下面只比较这部分文件数据缓存。

传统的双层缓存

1
2
3
4
5
6
7
8
       Host Page Cache
一份数据
/ \
拷贝 拷贝
/ \
Guest A Guest B
Page Cache Page Cache
再保留一份 再保留一份

Host 有一份,两个 Guest 又各有一份。

virtio-pmem + DAX

1
2
3
4
5
6
7
Guest A              Guest B
DAX DAX
\ /
\ 映射 /
\ /
Host Page Cache
共享同一份数据

两个 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
2
3
4
5
传统双层缓存:
Host 页面 → Guest Page Cache → 文件访问

virtio-pmem + DAX:
Host 页面 → DAX 文件访问

省掉的是 Guest 中间的那份文件数据缓存,不是所有内存开销。

3. 需要持久化写入时,单独发送 Flush

前面的例子只涉及读取,但 virtio-pmem 本身也支持写入。

需要区分:

1
2
3
数据已经写入映射页面
≠
数据已经持久化到后端存储

对于可写文件,Guest 应用通过 fsync() 或 msync() 等接口请求持久化,Guest 软件栈再通过 virtio-pmem 的 Flush 机制通知 Host。

1
2
3
4
5
6
7
8
9
Guest 写入数据
↓
Guest 请求持久化
↓
通过 virtqueue 发送 Flush
↓
Host 完成相应的持久化操作
↓
返回成功

规范要求:Flush 成功完成前,设备必须保证该请求之前已经完成的写入,能够在设备重置或掉电后保留。

因此,这里的 virtqueue 不是用来逐次搬运文件读写数据的,而是用来发送 Flush 请求。

DSec 中怎么使用?

DSec 将 virtio-pmem + DAX 用于只读的 EROFS 基础镜像和工具层,让同一 Host 上的多个 microVM 共享后端页面。

它并没有把所有磁盘都换成 virtio-pmem。论文(§5.2)指出,冷访问和页面管理元数据仍有开销;对于较大的可写磁盘,DSec 使用 DAMON 与 virtio-balloon 回收冷缓存。

两者可以这样区分:

1
2
3
4
5
virtio-pmem + DAX:
减少重复缓存

DAMON + virtio-balloon:
回收已经产生、但长期不用的缓存

记忆图

1
2
3
4
5
数据访问:
Guest → DAX → 映射的 Host 页面

写入持久化:
Guest → Flush 请求 → Host 持久化后端数据

一句话概括:

文件数据通过映射访问,避免在 Guest 中重复缓存;需要持久化的写入,再通过 Flush 通知 Host 完成。


参考资料:

  1. Virtual I/O Device (VIRTIO) Version 1.4 — §5.19 PMEM Device
  2. DeepSeek Elastic Compute (DSec): A Sandbox Infrastructure for Effective Agentic Training at Scale — §5.2、§8.4
  3. QEMU Documentation: VirtIO Persistent Memory