KVM/QEMU 和 guest OS 如何处理页面错误

How does KVM/QEMU and guest OS handles page fault

例如,我有一台启用了 KVM 的主机 OS(例如,Ubuntu)。我用 QEMU 启动虚拟机 运行 来宾 OS(比如 CentOS)。据说对于宿主OS来说,这个VM只是一个进程。所以在主机的角度来看,它像往常一样处理页面错误(例如,根据需要分配页面框架,必要时根据 active/inactive 列表交换页面)。

这是问题和我的理解。在来宾 OS 中,因为它仍然是一个成熟的 OS,我认为它仍然具有处理虚拟内存的所有机制。它看到 QEMU 提供的一些 虚拟化 物理内存。通过虚拟化物理内存,我的意思是来宾 OS 不知道它在 VM 中,并且仍然像在真实物理机器上一样工作,但它所拥有的确实是 QEMU 给出的抽象。因此,即使为其分配了一个页面框架,如果该页面不在客人的页面 table 中,客人 OS 仍将触发页面错误,然后将一些页面映射到该框架。更糟糕的是,可能会出现双页错误,来宾首先在页错误时分配一些页框,这会在主机 OS.

触发页错误

但是,我也听说过浅(或阴影)页面 table 之类的东西,它似乎可以优化这个不必要的双页错误和双页 table 问题。我还查看了其他一些内核实现,特别是 unikernels,例如 OSv, IncludeOS 等。我没有发现任何与页面错误和页面 table 机制相关的内容。我确实看到了一些像 page_fault_handler 这样的符号,但没有我在 Linux 内核代码中看到的那么大。在这些 un​​ikernel 实现中,内存管理似乎不是什么大问题。所以我假设 QEMU/KVM 并且一些英特尔的虚拟化技术已经解决了这个问题。

关于这个话题有什么想法吗?或者如果你对这个问题有一些好的references/papers/resources,或者一些提示会很有帮助。

QEMU/KVM有两种方式支持来宾物理内存:EPT 和影子页表。 (EPT是Intel定义的机制,其他处理器也支持类似的东西,这里就不说了。)

EPT 代表扩展页表。除了常规处理器页表之外,它是 CPU 支持的第二级分页。 运行 在 VM 中,常规页表用于将客户虚拟地址转换为客户物理地址,而 EPT 表用于将客户物理地址转换为主机物理地址。这种双级翻译是为来宾中的每个内存访问执行的。 (处理器 TLB 隐藏了大部分成本。)EPT 表由 VMM 管理,而常规页表由来宾管理。如果某个页面不在来宾页表中,则会在来宾中导致页面错误,正如您所描述的那样。如果页面存在于访客页面表中但不存在于 EPT 中,则会导致 EPT 违规 VM 退出,因此 VMM 可以处理丢失的页面。

当 EPT 不可用时使用影子页表。影子页表是访客页表的副本,它将 GVA 到 GPA 和 GPA 到 HPA 映射合并到一组页表中。当发生页面错误时,它总是会导致 VM 退出。 VMM 检查丢失的页面是否映射到客户页面表中。如果不是,则 VMM 将页面错误注入 客户机以供其处理。如果该页面映射到访客页面表中,则 VMM 会像处理 EPT 违规一样处理该错误。来宾中跨多个进程的影子页表的有效管理可能非常复杂。

EPT 实施起来更简单,而且对于大多数工作负载来说性能要好得多,因为页面错误直接生成给来宾 OS,这通常是它们需要处理的地方。影子页表的使用需要为每个页面错误退出 VM。但是,影子页表对于导致很少页面错误的一些特定工作负载可能具有更好的性能。