linux-kernel - KVM/QEMU和guest OS是如何处理page fault的

标签 linux-kernel kernel virtualization qemu kvm

例如,我有一个启用了 KVM 的主机操作系统(比如 Ubuntu)。我使用 QEMU 启动虚拟机来运行访客操作系统(比如 CentOS)。据说对于主机OS来说,这个VM只是一个进程。因此,从主机的角度来看,它会像往常一样处理页面错误(例如,根据需要分配页面框架,必要时根据事件/非事件列表交换页面)。

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

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

关于这个话题有什么想法吗?或者,如果您对这个问题有一些很好的引用资料/论文/资源,或者一些提示会非常有帮助。

最佳答案

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

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

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

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

关于linux-kernel - KVM/QEMU和guest OS是如何处理page fault的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60694243/

相关文章:

c++ - 哪个进程拥有给定端口(Linux 内核)?

c - 制作驱动程序时禁用 KBUILD_CFLAGS 选项

c - Linux 内核链表 - 和排序

ssh - 打包后 Vagrant 身份验证失败 - 私钥丢失

go - 如何连接到 docker/engine-api 中的远程套接字?

linux - 学习网络驱动程序接口(interface)的好链接

linux - 测试使用 VFIO 的用户空间驱动程序?

unix - 写入套接字是否是 sendfile() 系统调用的任意限制?

c - 缺少linux内核头文件

linux-kernel - Linux : How the guest VM's kernel maintain's time