完全仿真与完全虚拟化

use*_*444 31 virtualization emulation

在完全仿真中,I/O设备,CPU,主存储器被虚拟化.客户操作系统将访问虚拟设备而不是物理设备.但究竟什么是完全虚拟化?是完全仿真还是完全不同的东西?

Sam*_*eff 31

仿真和虚拟化是相关的,但不一样.

仿真使用软件来提供不同的执行环境或体系结构.例如,您可能在Windows机器上运行Android模拟器.Windows框没有与Android设备相同的处理器,因此模拟器实际上通过软件执行Android应用程序.

虚拟化更多的是在同一物理环境中运行的多个虚拟环境之间创建虚拟障碍.最大的区别是虚拟化环境是相同的架构.虚拟化应用程序可以提供虚拟化设备,然后将其转换为物理设备,并且虚拟化主机可以控制哪个虚拟机可以访问每个设备或设备的一部分.实际执行通常仍然是本机执行,而不是通过软件执行.因此,虚拟化性能通常比仿真性能好得多.

还有一个单独的虚拟机概念,例如运行Java,.NET或Flash代码的虚拟机.它们可以在一个实现与下一个实现之间变化,并且可以包括仿真或虚拟化或两者的方面.例如,JVM提供了执行Java字节代码的机制.但是,JVM规范并未规定字节代码必须由软件执行,或者必须将它们编译为本机代码.每个JVM都可以做到这一点,实际上大多数JVM都在适当的情况下使用仿真并在适当的地方使用JIT(我认为Hotspot JIT就是Sun/Oracle的JVM所称的).

  • @ use736468,即使它们没有以1:1显示,它们仍然可以虚拟化而不能模拟.例如,在VMWare中,您可以拥有一个连接到真实物理CD驱动器的虚拟化CD驱动器,或者您可以拥有一个由主机上的ISO映像支持的模拟CD驱动器.你也遇到了理论与实际差异的问题.除了像Android模拟器这样的纯模拟器或者那些旧的8位NES模拟器(我最喜欢的!),大多数产品实际上都包含虚拟化和仿真的某种组合. (2认同)

Jac*_*cob 7

在完全仿真中,I/O 设备、CPU、主内存被虚拟化。

不,它们是在软件中模拟的。模拟意味着他们的行为在软件中完全复制。

但究竟什么是完全虚拟化?

通过虚拟化,您可以尝试在硬件上运行尽可能多的代码以加快进程。对于必须在内核模式下运行的代码,这尤其是一个问题,因为这可能会改变主机(运行Hypervisor 或 VMM 的机器)的全局状态,从而影响其他虚拟机。

  • 澄清:http://www.computerworld.com/s/article/338993/Emulation_or_Virtualization_ (2认同)

Lew*_*sey 6

在此处输入图片说明

Hypervisor 是监督者的监督者,即它是控制内核的内核。

类型 1 与类型 2 与混合虚拟机管理程序

  • 类型 1 管理程序是一种旨在运行 VM 的操作系统。它直接安装在磁盘上,像任何操作系统一样从引导扇区执行;它是一个专为管理和运行虚拟机而构建的操作系统,这就是您可以在其上执行的所有操作(与操作系统一样,它可以是整体式的或微内核化的)。安装在其上的所有操作系统都作为来宾运行。
  • 类型 2 虚拟机管理程序是运行在旨在运行应用程序的操作系统之上的虚拟机管理程序,可以作为应用程序(完全仿真),或者通过使用驱动程序修改内核以提供操作系统功能来运行 VM(虚拟化),将自身安装在主机操作系统下方/旁边,并且主机操作系统继续运行(在软件虚拟化的情况下在环 0 和在支持硬件虚拟化的驱动程序的情况下在非 VMX 模式环 0 中)和挂钩的管理程序下面它以 VMX 非 root 模式(在硬件虚拟化的情况下)或环 1(在软件虚拟化的情况下)管理来宾,同时在适当的情况下传递给主机操作系统并调用和使用主机操作系统及其驱动程序访问硬件(这就是为什么它经常被描绘在主机操作系统之上)。主机操作系统上的 GUI 程序与驱动程序通信,每个 VM 有一个子进程,每个 vCPU 有一个线程。
  • 混合虚拟机管理程序是一种旨在运行应用程序和 VM 的操作系统。它可以在常规主机操作系统模式下运行,但它具有管理程序模式,在启动时将主机操作系统作为虚拟机管理程序之上的来宾加载,并且可以加载其他来宾。管理程序通常是一个微内核管理程序,这意味着硬件驱动程序是在主机操作系统(称为父分区)而不是管理程序中实现的(在 Hyper-V 上,集成服务组件驱动程序可以安装在其他来宾上以与主机操作系统通信驱动程序通过主机操作系统设置的 VMBUS 系统)。主机操作系统在具有 VMCS 的 VMX 非 root 模式下运行。理论上你可以得到一个半虚拟化的混合管理程序,但 KVM 和 Hyper-V 只支持硬件虚拟化,此外,你也可以拥有一个单一的混合管理程序,但不 没有多大意义,而且由于主机操作系统的存在,它只需要微内核化。混合虚拟机管理程序本质上是一种类型 1 虚拟机管理程序,可以分别引导到类型 1 虚拟机管理程序模式和主机操作系统模式。微内核化管理程序通常是混合的,因为使用的主机操作系统是已经安装的操作系统(并且微内核化管理程序功能已经是其中的一部分——它可以作为 Windows 服务器上的一个功能安装)

完全模拟的 2 类虚拟机管理程序

完整仿真器将目标 ISA 的所有寄存器作为变量进行仿真,并且 CPU 被完全仿真。这可能是因为想要模拟一个来宾,其 ISA 与主机的 ISA 不同(或者,如果您运行 x86 模拟器(例如 Bochs)并且碰巧在 x86 系统上运行它,则它确实可以相同;它不会没关系。正如 Peter 所说,模拟器不需要特权访问(ring 0 驱动程序助手),因为所有解释和模拟都是在进程本地完成的,并且进程调用常规主机 I/O 函数。这是有效的,因为没有任何代码需要本地运行。如果您希望它本地运行,则必须通过驱动程序将此功能带到ring 0)。完全仿真是对一切的仿真:CPU、芯片组、BIOS、设备、中断、页面遍历硬件、TLB。仿真器进程在环 3 中运行,但这对看到仿真/虚拟环(0 和 3)的来宾不可见,这些环将由解释器监视,并将通过根据指令为违规的寄存器变量分配值来模拟中断它正在解释、模仿 CPU 在每个阶段但在软件中会做的事情。仿真器从地址读取指令,对其进行分析,每次出现寄存器(例如 EDX)时,它将读取 EDX 变量(仿真 EDX)。它模仿 CPU 的操作,这很慢,因为通常由 CPU 透明处理的单个操作有多个操作。如果访客尝试访问虚拟地址,动态重新编译器使用这个来宾虚拟地址并使用 vCR3 遍历来宾页表(模仿 tlb 未命中页遍历器),然后使用其 cr3 的模拟器进程页表直接从 vCR3+来宾虚拟地址部分产生的每个物理地址中读取它无法控制,因为它是一个进程,并且就主机操作系统而言,物理地址只是进程中的一个虚拟地址(通过添加偏移量然后充当主机虚拟地址,来宾物理映射到主机虚拟地址,所以是一个隐式 P2M 表)。如果动态重新编译器在使用 vCR3 遍历时在来宾 PTE 上检测到无效位,则它会向来宾模拟页面错误,将地址放入 vCR2。

软件虚拟化类型 2 管理程序

完全虚拟化是一种类型 1 管理程序方案,实际上可以用于类型 2 管理程序,并且是前者的性能提升,并且只有在来宾 ISA 与主机 ISA 相同时才能使用。在 x86 上无法实现完全虚拟化,因为:

x86 架构中环 1 的实现存在某些从未修复的缺陷。某些应该陷入环 1 的指令不会。例如,这会影响 LGDT/SGDT、LIDT/SIDT 或 POPF/PUSHF 指令对。“加载”操作是有特权的,因此可以被捕获,而“存储”指令总是成功的。如果允许来宾执行这些,它将看到 CPU 的真实状态,而不是虚拟化状态。CPUID 指令也有同样的问题。

实际上,这也适用于环 3。这不仅仅是环 1 的故障。SGDT 等不是特权指令,但允许 VM 执行它与 Popek 和 Goldberg 的要求相矛盾,因为 VM 可以读取 CPU 的真实状态并获取真实 GDT 的地址而不是虚拟的。在 UMIP 之前,软件完全虚拟化在 x86 上是不可能的,在 Intel VT 之前,x86 CPU 本质上并不符合 Popek 和 Goldberg 的要求,因此必须使用半虚拟化。半虚拟化仍然不符合 Popek 和 Goldberg(因为只修补内核模式代码,所以可以使用 SGDT),但至少它可以工作,而完全虚拟化根本不起作用,因为 SGDT 会读取一个虚假值(主机 SGDT) 在来宾内核模式下,这意味着如果没有修补,使用 SGDT 的来宾内核代码将无法按预期工作。SGDT 在用户模式下可用至少不会损害主机操作系统,而 LGDT 肯定会。

VirtualBox 使用 ring 1 完全虚拟化,但半虚拟化了有问题的指令,尽管它们在 ring 1 中,但仍像在 ring 0 中执行一样,并且需要 ring 0 驱动程序的帮助;驱动程序充当管理程序。令人惊讶的是,关于如何实现类型 2 管理程序的信息很少。以下将是我对此事的最佳猜测——在给定硬件和主机操作系统操作的情况下,我将如何实现类型 2 管理程序。

在 Windows 上,我想当驱动程序启动时它会初始化符号链接并等待用户模式DeviceIoControl虚拟机软件发出用于启动虚拟机实例的IOCTL 。处理程序将执行以下过程: 驱动程序将处理程序注入 IDT 以用于一般保护故障。它可以通过用包装器KiInterruptDispatch替换KiInterruptTemplateIDT来放置包装器来实现这一点。在 Windows 上,它可以将包装器注入所有 IDT 条目,包括条目错误检查条目,但这意味着挂钩 IDT 写入例程以获取新中断。为了实现这一点,它可能会读取 IDTR 中的虚拟地址并写保护该区域,然后对 IDT 的主机更新将陷入管理程序 GPF 包装器中,该包装器将在写入的 IDT 条目处安装一个包装器。

但是,64 位 Windows 主机上的 64 位 Windows 客户机需要能够拥有自己的内核空间,但问题是,它将与主机内核结构位于完全相同的位置。因此,驱动程序需要擦除 virtualbox 进程的整个内核视图。这无法映射到访客或访客可见。它通过从 virtualbox 进程的 cr3 页面中删除条目来实现。virtualbox进程和其他宿主进程使用的GDT和IDT需要相同,但是为了避免保留guest虚拟地址,当guest写入IDTR时,hypervisor可以将其作为实际的IDTR值,但实际上将其在 SPT 中映射到主机使用的相同物理 4KiB IDT 帧。这意味着管理程序驱动程序在来宾和主机线程之间切换时需要更改 IDTR。因为映射 IDT 的来宾虚拟页面是写保护的,所以如果 cr3 是其来宾进程之一,那么来宾对此范围的任何写入都将由虚拟机管理程序记录在它构建的来宾 IDT 中。问题是当处理 ISR 时,它会跳转到一个没有映射到进程中的管理程序 RIP,因为驱动程序位于主机内核中;因此,需要将此包装器的 RIP 映射到 SPT。这意味着您不能在来宾中不保留任何虚拟内存,因此,您可能可以避免保留主机用于其 IDT 的 4KiB 地址范围,并以静默方式将来宾访问重定向到不同的主机物理页面然后不必在任务切换上更改 IDTR。将读取和写入重定向到不同的主机物理页面,这在中断后不会发生,因为它将在环 0 中,因此 IDT 中的跳转将在映射到它的真实主机物理页面中,因为它没有GPF,因此管理程序无法重定向),因此来宾不知道该区域已被保留。每个 IDT 条目都有一个不同的包装器,它将调用一个主处理程序,该处理程序也需要映射并传递 IDT 条目代码。处理程序将在寄存器中传递 cr3,将 cr3 更改为映射主机内核的虚拟进程,然后它将调用主处理程序。处理程序检查 cr3,如果它是来宾影子 cr3 或主机 cr3,并执行适当的操作。

驱动程序也必须以同样的方式将自己注入时钟中断——如果时钟中断触发,来宾状态或主机状态(包括当前的 cr3)被推送,并且管理程序处理程序将推送来宾 IDT 的地址时钟中断到它管理的所有 vCPU 线程的内核堆栈上(模拟 CPU 会做什么)在一个新的陷阱帧中,如果没有一个已经存在,然后在将 cr3 更改为映射主机的一个后调用原始主机处理程序核心。这将确保每次在主机上调度时在来宾中进行上下文切换,因此来宾时钟间隔将大致匹配主机时钟间隔。

完全虚拟化将被称为“trap and emulate”,但它不是完全模拟,因为所有 ring 3 代码实际上都在主机 CPU 上运行(而不是完全模拟,其中运行的代码是读取行的解释器) . 此外,TLB 和页面遍历硬件实际上是直接使用的,而在仿真器上,如果在软件中模拟的 TLB 阵列中不存在,则每次内存访问都需要在软件中进行遍历。只有特权指令和寄存器、中断、设备和 BIOS 被模拟到来宾 - 部分模拟 - 模拟仍然发生,但是当任何数量的代码在本机运行时,它被称为虚拟化(完整的、平行的或硬件的)协助)。

当访客陷入访客操作系统时,它将使用 INT 0x2e 或系统调用。管理程序显然在 0x2e 处为 INT 注入了一个包装器,它将在SYSENTER_CS_MSR:SYSENTER_EIP_MSRfor sysenter 或IA32_LSTARMSR 处为 syscall插入一个处理程序。MSR 中的处理程序需要映射到 SPT 中,并将检查 cr3 是否是来宾进程之一的影子,如果不是,则不需要更改 cr3,因为当前将包含主机内核并跳转到主机处理程序。如果是guest进程的cr3,它会将cr3更改为一个dummy进程(可能是一个专门用于映射主机内核的IO任务的virtualbox主机进程)并跳转到一个主处理程序,在它构建的guest IDT中传递RIP到通过和半虚拟化的重新编译器/修补程序某些不能保证捕获的指令,用跳转到管理程序内存永久替换它们,在那里它放置更好的代码(这将导致保护错误,因为它们在 SPT 中响铃 0),直到它到达 IRET 或 sysexit 等,然后它将 cr3 改回来宾的 cr3 并在将堆栈上的 ring 1 特权放入它已构建的来宾 IDT 中的 RIP 后执行 IRET,然后执行实际的来宾 ISR。当由于在环 1 中执行环 0 指令或发生插入的半虚拟化陷阱而发生陷阱时,在通用保护故障条目/管理程序 ISR 处注入的 ISR 将确保 cr3 属于来宾进程,并且它将声明并处理问题,如果不是,则 cr3 不会 不需要更改为包含主机内核的内核,以便将控制权传递给主机处理程序,因为它将处于非客户进程的上下文中。可能发生这种情况的一个实例是来宾写入 cr3 以进行来宾上下文切换。这需要模拟,因为来宾不能执行此指令并修改 cr3,因为它会更改主机操作系统上的主机进程的 cr3;管理程序需要开始写入并写入新的影子 cr3 而不是来宾想要的 cr3。当来宾读取 cr3 时,此机制阻止来宾读取真实的 cr3,管理程序将来宾插入的 cr3 的值(不是影子)插入请求的寄存器,将下一条指令地址插入堆栈并恢复执行对它所在的戒指感到愤怒。

来宾 I/O 将针对来宾物理地址空间,该地址空间映射到虚拟机管理程序中定义的模拟设备的虚拟缓冲区和寄存器。这些模拟寄存器(例如门铃寄存器)将在主机上下文中定期检查(例如时钟中断挂钩),其方式与设备对硬件寄存器更改的反应完全相同,并且处理程序将决定是否需要中断模拟(将中断推送到代表所选 vCPU 的线程的内核堆栈上,以根据模拟配置空间中来宾分配的 MSI 向量进行中断),或者,由于模拟寄存器写入,需要使用构造 I/O 操作本机 Windows API 函数到来宾指定的缓冲区(翻译 GVA->

至于在半虚拟化 64 位类型 2 管理程序上进行分页,这是一个棘手的问题。硬件使用影子页表 (SPT),它是 GVA 到 HPA 的映射。我最好的猜测是,管理程序驱动程序从 virtualbox 进程的锁定页面中为每个 GP 故障(在环 1 中执行环 0 指令)中选择一个影子 cr3 页面,它看到一个新的来宾分配的 cr3 地址被写入 cr3。它将这个来宾选择的地址与管理程序选择的影子 cr3 页面的地址配对,并将虚拟机进程 cr3 更改为影子 cr3 的进程,而不是试图写入的来宾进程。该阴影CR3页面(你会看到用无处不在来宾页表是写保护的,但它肯定是错误的,因为它是在 CPU 上运行的影子页表,因此是唯一会导致保护错误的页表;使用影子 cr3 而不是来宾 cr3)由内核驱动程序写保护(这是由递归 PML4 条目中的读/写位完成的)。来宾尝试使用的某些 GPA 的 cr3 页面将由虚拟机管理程序转换为其关联的 HPA,并且 cr3 页面中的条目将复制到影子 cr3,并且 PML4E 中的 GPA 地址将使用以下命令转换为 HPA P2M 表。每次guest通过虚拟地址把这个写到guest cr3 page时,这个虚拟地址永远是shadow cr3 page的,而不是guest cr3 page的,并且会因为写保护位和处于ring 1而出错.VirtualLock为虚拟机锁定指定数量的 RAM)(管理程序可以维护 P2M(来宾帧到主机帧映射)和来宾页表(来宾虚拟页到来宾​​帧映射)的虚拟 TLB,它可以在执行之前检查软件页面遍历,而硬件维护 SPT 的 TLB)。然后管理程序将检查虚拟 TLB,以将 CR2 GVA 快速转换为 GPA;如果不存在,它将跟踪来宾页表(通过其 HVA 访问来宾 cr3(使用 P2M 转换 GPA->HPA,然后使用内核函数转换 HPA->HVA))并根据来宾的需要写入条目尝试来宾 GPA。当页面错误发生时,处理程序检查影子 cr3 是它的来宾进程之一并检查 SPT(使用 Windows 内核函数获取与故障 GVA 关联的条目的虚拟地址,就好像它是一个常规进程一样),然后使用来宾 cr3 遍历来宾页表与当前的 cr3 相关联,解析出错的 SPT 虚拟地址(转换 GPA -> HPA -> HVA)。如果影子 PTE 无效,则它是影子页面错误。如果访客 PTE 也是无效的,那么它会使用访客 IDT 的页面错误条目中的地址的 RIP 将其压入堆栈来模拟中断;在执行此操作之前,它会如前所述修补重新编译器中的代码(当客户在中断期间从其页表中读取时,它实际上将读取 SPT,因此 SPT 需要使用管理位进行读取保护,以便它可以被拦截,并且可以从故障内存访问中的地址读取来宾页表条目)。对于发生的任何其他中断,即主机设备,它并不适用于来宾,因此如果处理程序看到当前的 cr3 属于其一个来宾的进程,它会将 cr3 更改为包含主机内核的虚拟进程映射并调用原始KiInterruptTemplate对于主机处理程序;主机处理程序完成后,它将替换 cr3。

硬件辅助的 2 类虚拟机管理程序

硬件辅助类型 2 是性能的进一步提升,使情况变得不那么复杂,并将其统一到一个界面中,并自动执行许多需要即兴创作的临时 cr3 杂耍和管理任务,使其更加清晰。内核驱动程序只需要执行 vmxon,等待来宾向驱动程序注册,然后所有 VM 退出事件将由一个统一的处理程序在 RIP 和 CR3 处处理,它插入到 VMCS 主机状态(意味着处理程序存根不需要映射到来宾内核虚拟地址空间)。它是专门为此设计的,与环 1 不同,这意味着不需要重新编译器(代码扫描和分析管理器 (CSAM) 和补丁管理器 (PATM))。它还具有诸如 TSC 缩放和 TSC 偏移字段之类的内容,可供使用 TSC 进行更公平调度的访客使用。管理程序仍然挂钩时钟中断以执行 I/O 更新,如果当前执行的线程是其 vCPU 的线程地址,它将需要 vxmoff(这将导致 VM 退出)并推送某些线程的地址主机内核内存中的重新初始化序列将 vmxon 和 vmresume 绑定到 vCPU 的 VMCS,其中包含来宾保存的状态(但模拟时钟中断准备好执行,其代码将使用 RDTSC,它将 VM 退出和偏移量虚拟机管理程序可以使用 VMCS 来报告一个值,该值考虑了来宾未在主机上安排的时间,即从中减去主机时间以使主机不可见)。它没有

如果支持 EPT,则来宾选择的物理地址(cr3、IDTR 等)和页表以 vmx 非 root 模式在实际硬件上运行。GVAs 被转换为 HPA 如下:客机 CR3 用于产生 PDPT 的 GPA,然后使用客机的 EPTP 运行整个 EPT 以最终获得 PDPT 的 HPA,依此类推(它是与使用来宾页表和 P2M 的软件虚拟化相同的过程,除了页遍历是在实际的页遍历硬件上完成的,这更快)。当出现页面错误时,不会发生 vm 退出,并且存在来宾选择的 IDTR,因此使用来宾 IDT 将中断作为非根环 0 处理。来宾可以更新此映射,虚拟机管理程序不需要干预。当重新尝试访问时,EPT 故障将导致 VM 退出,其 EPTP 等效于 cr2 以及指向来宾的虚拟机管理程序 EPTP 的指针。然后它将其映射和 VMRESUME 更新为故障指令的 RIP。


Pet*_*des 5

无需仿真或虚拟化,代码就可以直接在硬件上运行。其指令由CPU本地执行,其I/O访问直接访问硬件。


虚拟化是指来宾代码至少在某些时候以本机方式运行,并且仅捕获在虚拟机(例如管理程序)外部运行的主机代码以进行特权操作或 I/O 访问。

为了处理这些陷阱(也称为虚拟机退出),虚拟机实际上可能会模拟来宾试图执行的操作。例如,来宾可能正在运行一个简单网卡的设备驱动程序,但 NIC 纯粹是在虚拟机中的软件中实现的。如果虚拟机使用直通将来宾的 I/O 访问发送到主机上的真实网卡,那么这将是该硬件的虚拟化。(特别是如果它以允许多个来宾同时使用它的方式进行,否则它实际上只是将其提供给一个来宾,而不是虚拟化它。)

对虚拟化的硬件支持(例如 Intel 和 AMD 的单独 x86 虚拟化扩展)可以让来宾执行通常会影响整个计算机的操作,例如修改页表中的内存映射。因此,CPU 不是触发 VM 退出并让 VM 弄清楚来宾正在做什么,然后从外部修改内容以实现结果,而是内置了一个额外的转换层。(有关更多信息,请参阅链接的 wiki 文章)基于软件的虚拟化与硬件辅助虚拟化的更好但更长的描述。)


纯模拟意味着来宾代码永远不会在本机运行,也永远不会看到主机的“真实”硬件。 模拟器不需要对主机的特权访问。(有些人可能需要对主机进行特权访问以实现设备直通,或者使用原始网络套接字让来宾看起来像是真正连接到与主机相同的网络)。

在 x86 主机上运行的 ARM 模拟器始终必须以这种方式工作,因为主机硬件首先无法运行 ARM 指令。

但例如,您仍然可以在 x86 主机上模拟 x86 来宾。来宾和主机架构匹配这一事实并不意味着模拟器必须利用这一事实。


例如,BOCHS 是一个用可移植 C++ 编写的 x86 PC 模拟器。它的主要用途之一是调试引导加载程序和操作系统。

BOCHS 并不关心它是否在 x86 主机上运行。它只是一个读取二进制文件(磁盘映像)并在窗口中绘制(来宾视频内存的内容)的 C++ 程序。对于主机而言,它与 JPG 查看器或游戏没有特别不同。

一些模拟器使用二进制翻译将来宾代码即时编译为主机代码,但这仍然是模拟,而不是虚拟化。请参阅http://wiki.osdev.org/Emulator_Comparison

BOCHS 相对较慢,因为它直接读取和解码客户指令,而不进行二进制翻译。但它试图尽可能有效地做到这一点。请参阅Bochs 的工作原理,了解它用于有效跟踪来宾状态的一些技巧。由于仿真是在非 x86 硬件上运行 x86 软件的唯一选择,因此拥有高性能仿真器非常有用。BOCHS 有一些非常聪明且经验丰富的模拟器开发人员致力于此工作,特别是 Darek Mihocka,他在他的网站上有一些关于优化模拟的有趣文章。