X86虚拟化Linux系统中EPT PTE与宿主PTE的关联及脏页疑问
首先得明确一个核心点:EPT页表和宿主Linux的页表是完全独立的地址翻译结构,这也是虚拟化实现客户机与宿主隔离的关键之一。
为什么客户机写入不会触发宿主PTE脏位?
当你的虚拟机运行在VMX非根模式时,CPU会使用EPT完成客户机虚拟地址(GPA)到宿主物理地址(HPA)的翻译,全程绕开宿主的页表(宿主页表仅用于翻译自身虚拟地址HVA到HPA)。客户机的写入操作只会触发EPT条目的脏位(CPU自动置位,前提是EPT条目允许写入),但这个过程中CPU根本没有访问宿主的PTE,自然不会更新宿主PTE的脏位。
你的观察是完全正常的:EPT脏位记录的是客户机对该页面的修改,而宿主PTE脏位只记录通过宿主地址空间(比如内核或宿主用户态进程)对页面的修改。
Linux如何识别脏页?
Linux内核的脏页标记(PTE中的脏位)是针对宿主自身地址空间内的页面访问设计的:
- 当内核或宿主用户态进程写入某个页面时,CPU会在访问宿主PTE的过程中自动置位脏位(如果PTE原本允许写入且脏位未置)。
- 内核利用这个脏位管理内存:比如决定哪些页面需要刷回磁盘、哪些可以被换出到swap等。
但对于hypervisor分配给客户机的页面,内核并不知道客户机对这些页面做了修改——因为这些修改没有通过宿主的地址空间发生,CPU不会更新宿主PTE的脏位。如果你的hypervisor需要跟踪这些页面的脏状态,必须依赖EPT的脏位,而非宿主PTE。
关于你的页表遍历代码的小提示
你用current->mm来获取页表,但current指向的是宿主当前运行的进程(可能是用户态进程或内核线程)。而你用kmalloc分配的页面属于内核虚拟地址空间,对应的页表应该用init_mm(内核全局页表)来遍历,而不是current->mm。不过即使你修正了这一点,依然看不到宿主PTE的脏位——因为客户机的写入根本没有触发宿主PTE的脏位更新。
后续处理建议
如果你的hypervisor需要实现脏页追踪类功能(比如快照、内存迁移),正确的做法是:
- 初始设置EPT条目为只读,当客户机触发EPT写违例时,标记该页面为脏,再将EPT条目改为可写,允许客户机继续执行。
- 定期扫描EPT页表,收集所有置位脏位的条目,这些就是客户机修改过的页面。
内容的提问来源于stack exchange,提问作者wangt13

