在完全仿真中,I/O设备、CPU和主存储器都是虚拟化的。客户操作系统将访问虚拟设备而不是物理设备。但是什么是完全虚拟化?它与完全仿真是相同的还是完全不同的东西?
在完全虚拟化中,虚拟机会虚拟化所有的硬件,包括CPU、内存和I/O设备。客户操作系统只能访问虚拟设备,而无法直接访问物理设备。与之相反,在完全仿真中,所有的硬件都被模拟出来,包括CPU、内存和I/O设备。客户操作系统访问的是模拟的硬件,而非真实的硬件。因此,完全虚拟化和完全仿真是两个不同的概念。在完全仿真中,I/O设备、CPU和主存储器都是虚拟化的。客户操作系统将访问虚拟设备而不是物理设备。但是什么是完全虚拟化?它与完全仿真是相同的还是完全不同的东西?
在完全虚拟化中,虚拟机会虚拟化所有的硬件,包括CPU、内存和I/O设备。客户操作系统只能访问虚拟设备,而无法直接访问物理设备。与之相反,在完全仿真中,所有的硬件都被模拟出来,包括CPU、内存和I/O设备。客户操作系统访问的是模拟的硬件,而非真实的硬件。因此,完全虚拟化和完全仿真是两个不同的概念。仿真和虚拟化是相关但并不相同的。
仿真使用软件提供不同的执行环境或体系结构。例如,您可能在Windows计算机上运行Android模拟器。由于Windows计算机没有与Android设备相同的处理器,因此模拟器实际上通过软件执行Android应用程序。
虚拟化更多地关注在同一物理环境中创建多个虚拟环境之间的虚拟隔离。最大的区别是虚拟化环境具有相同的体系结构。虚拟化应用程序可能提供虚拟设备,然后将其转换为物理设备,并且虚拟化主机可以控制每个虚拟机对每个设备或设备部分的访问权限。实际的执行通常仍然是本地执行,而不是通过软件执行。因此,虚拟化性能通常比仿真好得多。
还有一个独立的概念,即虚拟机,例如运行Java、.NET或Flash代码的虚拟机。它们可能因实现而异,并可能包括仿真或虚拟化或两者兼而有之的方面。例如,JVM提供了一种执行Java字节码的机制。但是,JVM规范并不规定字节码必须由软件执行或必须编译为本机代码。每个JVM都可以做自己的事情,实际上大多数JVM在适当的情况下都使用仿真和JIT的组合(我认为Sun / Oracle的JVM称之为Hotspot JIT)。
虚拟机监控器(Hypervisor)是指督导其他督导者的督导者,即它是控制内核的内核。
第一类、第二类和混合型虚拟机监控器
DeviceIoControl
发出IOCTL以启动虚拟机实例。处理程序将执行以下过程:驱动程序将一个处理程序注入通用保护故障中断表(IDT)。它可以通过在IDT中替换KiInterruptTemplate
来将包装器放置在KiInterruptDispatch
周围从而完成此操作。在Windows中,它可以向所有IDT条目(包括bug check条目)注入一个包装器,但这意味着钩入新中断的IDT写入例程。为了实现这一点,它可能会读取IDTR中的虚拟地址并写入保护该区域,然后主机更新IDT将陷入超级管理程序GPF包装器,它将在被写入的IDT条目上安装一个包装器。 SYSENTER_CS_MSR:SYSENTER_EIP_MSR 中插入处理程序以用于sysenter或者在 IA32_LSTAR MSR中插入处理程序以用于syscall。 MSR中的处理程序需要映射到SPT中,并检查cr3是否为其中一个客户进程的shadow,如果不是,则无需更改cr3,因为当前内容将包含主机内核,并跳转到主机处理程序。如果是一个客户进程的cr3,则将其更改为虚拟盒子主机进程(可能专门用于IO任务的),并跳转到主处理程序,将RIP传递给它构建的重编译器/修补程序,后者通过paravirtualizes某些不保证会捕获的指令,将其永久替换为跳转到hypervisor内存中放置更好代码的指令(由于它们在SPT中属于ring 0,因此会导致保护故障),直到达到IRET或sysexit等指令,然后将cr3切换回客户的cr3,并在将ring 1特权放入堆栈中向它构建的guest IDT的RIP执行IRET后,实际的guest ISR才会执行。当由于在ring 1中执行ring 0指令或插入的paravirtualized陷阱而引发陷阱时,注入到通用保护故障入口/ hypervisor ISR的ISR将确保cr3是客户进程的,然后它将声明并处理问题。如果不是,则cr3无需更改为包含主机内核的一个,以便将控制权传递给主机处理程序,因为它将处于非客户进程的上下文中。这可能发生的一个示例是guest写入cr3以进行guest上下文切换。这需要进行仿真,因为guest不能执行此指令并修改cr3,因为这将更改主机操作系统上主机进程的cr3; hypervisor需要截获写入并写入新的shadow cr3而不是guest想要的cr3。当guest读取cr3时,此机制会防止guest读取真实的cr3,并且hypervisor会将guest插入的cr3值(而不是shadow值)插入所请求的寄存器中,在堆栈中插入下一个指令地址,并使用iret恢复执行并返回到相应的ring中。
客户机I/O将针对映射到在虚拟缓冲区和模拟设备注册表中定义的客户机物理地址空间。这些模拟的寄存器(例如门铃寄存器)将以与设备响应硬件寄存器更改相同的方式在主机上下文中定期检查,处理程序将决定是否需要模拟中断(将中断推入表示所选vCPU的线程的内核堆栈以基于MSI向量由客户在模拟配置空间中分配)或者由于模拟寄存器写入,需要使用本机Windows API函数构建I/O操作,以访问客户指定的缓冲区(将GVA->HPA翻译并允许真实硬件写入客户缓冲区将使用的物理页面)。
关于在虚拟化的64位类型2超级监控程序上进行分页,这是一个棘手的问题。硬件使用一个影子页表(SPT),将GVA映射到HPA。我最好的猜测是,超级监控程序驱动程序为每个GP故障(在第1环执行第0环指令)选择一个来自virtualbox进程的锁定页面的影子cr3页面,当它看到新的客户分配的cr3地址被写入cr3时。它将此客户选择的地址与超级监控程序选择的影子cr3页面的地址配对,并更改virtualbox进程的cr3为影子cr3而不是尝试写入的客户cr3。 内核驱动程序通过递归PML4项中的读/写位将某些GPA的cr3页面保护为只读(以防止写入), 该 影子cr3页面(您将在 everywhere 中看到写保护的客户页表,但这肯定是错误的,因为在CPU上运行的是影子页表,因此只有影子页表才能引起保护故障; 使用影子cr3而不是客户端cr3),然后,客户尝试使用的特定GPA的cr3页面将由超级监控程序转换为其关联的HPA,并且cr3页面中的条目将复制到影子cr3中,PML4E中的GPA地址将使用P2M表将转换为HPA。每次客户端尝试通过虚拟地址将此写入客户端cr3页面时,此虚拟地址将始终是影子cr3页面而不是客户端cr3页面,并且由于写入保护位和在第1环中,它将导致故障。然后,在产生一般保护故障时注入的处理程序将查看其客户进程之一的影子cr3,并在与其实际发生保护故障的位置相同的SPT中执行概念上尝试在客户PTE中执行的写操作,并插入主机物理地址而不是尝试写入的客户物理地址(使用P2M TLB或P2M进行转换;我认为P2M在启动VM时填充,因为VirtualBox进程使用 VirtualLock
锁定指定量的RAM用于虚拟机)(超级监控程序可以维护P2M的虚拟TLB(从guest frame到host frame的映射)和客户页表(从guest virtual page到guest frame的映射),在执行软件页行走之前可以检查它们,而硬件维护SPT的TLB)。然后,超级监控程序将检查虚拟TLB以快速将CR2 GVA转换为GPA;如果不存在,则它将通过访问其HVA(使用P2M将GPA->HPA然后HPA->HVA的内核函数进行转换)来跟踪客户页表,并按照客户端所需的方式写入条目。当发生页面故障时,处理程序检查影子cr3是否属于其客户进程之一,并检查SPT(使用Windows内核函数获取与故障GVA相关联的条目的虚拟地址,就像它是一个常规进程一样),然后使用与当前cr3关联的客户cr3遍历客户页表,解析引起故障的SPT虚拟地址(将GPA -> HPA -> HVA进行转换)。如果影子P
硬件辅助型2级超级监控程序
硬件辅助型2级超级监控程序是性能的进一步提升,将情况变得不那么复杂,并将其统一为单个接口,并自动化许多需要临时CR3跳转和管理任务的操作,使其更加清晰。内核驱动程序只需要执行vmxon,等待客户端向驱动程序注册,然后所有VM退出事件都将由插入到VMCS主机状态中的RIP和CR3的统一处理程序处理(这意味着处理程序存根不需要映射到客户机内核虚拟地址空间中)。它是专门为此设计的,不像ring 1,因此不需要重新编译器(代码扫描和分析管理器(CSAM)和补丁管理器(PATM))。它还具有TSC缩放和TSC偏移字段之类的东西,可以供使用TSC进行公平调度的客户端使用。超级监控程序仍然钩取时钟中断以执行I/O更新,并且如果当前正在执行的线程是其vCPUs之一的线程的地址,则需要vxmoff(这将引起VM退出),并将一些重新初始化序列的地址推入主机内核内存中,该序列将vmxon和vmresume与具有其中保存的客户端保存状态的VMCS绑定的vCPU(但是具有已准备执行的模拟时钟中断,其代码将使用RDTSC,该时钟中断将VM退出,并且VMCS中的偏移量可以由超级监控程序用于报告经过时间来计算客户端未在主机上调度的值,即从中减去主机时间以使主机不可见)。它不需要更改cr3,因为vmxoff会自动执行此操作,因此现在可以将其传递给主机处理程序以执行主机操作系统的时钟中断处理过程。
如果支持EPT,则客户端选择的物理地址(cr3、IDTR等)和页表在vmx非根模式下运行的实际硬件上。GVAs翻译为HPAs的方法如下:使用客户端CR3产生PDPT的GPA,然后通过整个EPT运行使用客户端的EPTP最终获得PDPT的HPA等等(这与软件虚拟化的客户端页表和P2M相同,只是页面步进在实际的页面步进硬件上完成,速度更快)。当发生页面错误时,不会发生vm退出,并且客户端选择的IDTR存在,因此使用客户端IDT作为非根ring 0处理中断。客户端可以更新此映射,超级监控程序无需干预。在重新尝试访问时,EPT故障将导致VM退出,并具有与cr2等效的EPTP和指向客户端的监控程序EPTP的指针。然后它将更新其映射并VMRESUME到出现故障的指令的RIP。
没有仿真或虚拟化,代码直接在硬件上运行。其指令由CPU本地执行,其I/O访问直接访问硬件。
BOCHS比较慢,因为它直接读取和解码客户指令,而不进行二进制翻译。但它尽可能地高效运行。请参阅How Bochs Works Under the Hood以了解它使用的一些技巧来有效地跟踪客户状态。由于仿真是在非x86硬件上运行x86软件的唯一选择,因此拥有高性能仿真器非常有用。BOCHS有一些非常聪明和经验丰富的仿真器开发人员在开发中,特别是Darek Mihocka,在他的网站上有一些有趣的文章on his site关于优化仿真。
这是我自己尝试回答的问题。
虚拟化作为一个概念,使得多个/不同的应用程序可以在相同的底层硬件上共存,而彼此之间并不知道对方的存在。
例如,完整的操作系统如Windows、Linux、Symbian等及其应用程序可以在同一平台上共存。所有计算资源都被虚拟化了。
这意味着上述任何一台机器都无法访问物理资源。唯一能够访问物理资源的实体是一个名为虚拟机监视器(也称为超级监视器)的程序。
现在这很重要,请仔细阅读和反复阅读。
超级监视器为上述每台机器提供了一个虚拟化环境。由于这些机器访问的不是物理硬件,而是虚拟化硬件,因此它们被称为虚拟机。
举个例子,Windows内核可能想要启动一个物理计时器(系统资源)。假设该计时器是内存映射IO。Windows内核在计时器地址上发出一系列Load/Store指令。在非虚拟化环境中,这些Load/Store将导致计时器硬件的编程。
然而,在虚拟化环境中,这些基于Load/Store的物理资源访问将导致陷阱/故障。陷阱由超级监视器处理。超级监视器知道Windows试图编程计时器。超级监视器为每个虚拟机维护计时器数据结构。在这种情况下,超级监视器更新了为Windows创建的计时器数据结构。然后它编程真正的计时器。计时器生成的任何中断都首先由超级监视器处理。更新虚拟机的数据结构并调用后者的中断服务例程。
长话短说,Windows在非虚拟化环境中所做的一切都做到了。在这种情况下,它的操作导致更新的不是真实的系统资源,而是虚拟资源(上述数据结构)。因此,所有虚拟机都认为它们正在访问底层硬件;实际上,所有对物理硬件的访问都是通过超级监视器进行调解的。
以上所述的一切都是关于完全/经典虚拟化的。大多数现代CPU不适合进行经典虚拟化。陷阱/故障并不适用于所有指令。因此,现代设备上的超级监视器很容易被绕过。
这就是Para-virtualization产生的地方。虚拟机源代码中的敏感指令被替换为对Hypervisor的调用。上面的load/store片段可以被类似以下的调用所替换:
Hypervisor_Service(Timer Start, Windows, 10ms);
仿真是与虚拟化相关的主题。想象一种情况,原本为ARM编译的程序被制作成在ATMEL CPU上运行。ATMEL CPU运行一个仿真器程序,解释每个ARM指令并在ATMEL平台上模拟必要的操作。因此,仿真器提供了一个虚拟化环境。
在这种情况下,系统资源的虚拟化不是通过陷阱和执行模型来执行的。
虚拟化和仿真基本上是相同的东西。这两个词所暗示的一个概念是相同的,即这两个词是同一事物的方面。这在QEMU中得到了证明,它是一个快速的仿真器,可以执行硬件虚拟化。
你可以把这个事物看作是模拟。但模拟这个词也可能会让人感到困惑。
首先我们可以定义这些词的共同含义。
现在我们展示这些词的意思基本相同。例如,在模拟中,您正在使用另一个系统创建一个系统的副本。这是仿真的常见含义。在虚拟化中,您希望您的虚拟化系统像真实系统一样运行。也就是说,理想情况下它像一个副本,即使它可能被不同地实现,并且可能不会完全“模拟”硬件。这与模拟基本相同。在仿真中,您模拟另一个系统等。
因此,我们可以看到这些词有些可以互换。其基本概念是模拟。
在虚拟化中,例如操作系统虚拟化(“虚拟机”),我们正在创建一个像操作系统一样运行的系统。它可能使用底层硬件、虚拟机监视器或其他东西的技巧来提高性能和安全性。但最终它只是操作系统的模拟。通常使用“虚拟机”一词时,它并不是机器的精确副本(如模拟器)。它只做足够的工作,以允许程序在真实操作系统上按预期运行。
在仿真中,通常意味着模拟是“精确的”。在硬件仿真中,您复制了硬件系统的所有功能。这意味着您已经创建了硬件的仿真。您可以说您创建了硬件的虚拟化,但这里虚拟化略有不同。虚拟化意味着创建一个隔离的环境,而仿真并不一定意味着这一点。因此,硬件仿真器可能会为硬件提供与硬件本身相同的接口,但仿真器的实现可能依赖于全局内存,因此如果您尝试同时运行两个仿真器,它们将互相干扰。这就是虚拟化解决的问题,它隔离了仿真。一个更近期的回应:
通过我的研究,我可以说这是一个更好的回应来理解概念如何出现:
仿真的第一个概念实际上可以追溯到第一台计算机Colossus。它在1941年被英国政府用于模拟纳粹恩格玛密码机的功能。仿真理论于1962年开发,并由三名IBM工程师从三个不同的角度构思。
仿真意味着模仿目标的行为,可以是硬件,例如emu8086仿真器,也可以是软件,例如模拟来自某个网络端口的服务。
您想模仿目标提供的一组功能,也许您对内部机制不感兴趣。
为什么要这样做?为了控制这些功能。为什么要控制?有很多原因,这是一个非常大的主题,这里无法讨论。但请记住,您想处于事物的背后。
但是这样的过程对性能来说是昂贵的。您有一条指令,需要执行很多其他指令。也许您只对其中一些指令进行控制感兴趣。因此,我们希望允许一些指令以本地方式执行。
那么当所有这些指令执行变得本地化时会发生什么?那么你就有了理想的虚拟化。你可以虚拟化任何软件,但今天的趋势是从操作系统的虚拟化转向应用程序的虚拟化。我之所以说是理想的,是因为这种软件在每个硬件上都有不同的执行方式,因此还需要模拟一些指令。重要的是要理解,今天大多数虚拟化技术不仅仅是关于虚拟化,也涉及到仿真。
另外请注意,在我们从仿真过渡到虚拟化的过程中,系统的输入被减少了,因为虚拟化只接受软件作为输入。控制这些指令流的控制器称为超级监视器。
虚拟化可能发生在计算机体系结构的不同层次上,这些层次(从高到低)为:1:应用程序,2:库,3:操作系统,4:硬件抽象层(HAL),5:指令集架构(ISA)。 在后一层下面是硬件。
通常,某个层通过利用较低层在其接口中公开的指令来利用较低层提供的服务。
请注意,服务的使用与分层并不严格相关,因为某些层可以跳过下面的层,并利用从较低层获取的指令。例如,应用程序可以直接向HAL层提供某些指令,跳过库和操作系统层。
我认为将虚拟化与仿真相对立是一种常见的误解,因为它们是不可比较的。当人们谈论虚拟化时,他们大多数想到的是type 2 hypervisors所做的事情。
根据wikipedia的说法,虚拟化是:
虚拟化(有时缩写为v12n)是创建某物的虚拟(而非实际)版本的行为,包括虚拟计算机硬件平台、存储设备和计算机网络资源。
这个定义适用于仿真和类型2虚拟机。因此,仿真器是虚拟化的一个子类型,而类型2虚拟机是另一个子类型。两者都可以让您运行虚拟机,但它们的工作方式和使用方式通常不同。许多虚拟机实际上依赖于这两种技术来实现其目标。
此外,模拟并不总是完全复制原始硬件1:1(这是设计上的,而不是缺乏文档),例如DOSBox模拟了一种实际不存在的PC类型,或者高级模拟器(如旧版Ultra HLE)。这使得模拟器更加高效(但有破坏软件兼容性的风险)。其他模拟器也会出于不同目的这样做:扩展原始硬件的功能(例如dolphin可以让您在4K下运行游戏,或者PS1模拟器可以显著提高3D质量,或者最近,一个带有修改PPU的SNES模拟器可以输出16:9图形,并用于修补过的超级马里奥世界以在宽屏下运行)。