目录
概述
传统的虚拟化技术一般是通过陷入再模拟的方式实现的,而这种方式依赖于处理器的支持。也就是说,处理器本身是否是一个可虚拟化的体系结构。
不论采用何种虚拟化方式,VMM(Virtual Machine Monitor,虚拟机监视器)对物理资源的虚拟可以归结为三个主要任务:处理器虚拟化、内存虚拟化和I/O虚拟化。
可虚拟化架构与不可虚拟化架构
一般来说,虚拟环境由三个部分组成:硬件、VMM和虚拟机。在没有虚拟化的情况下,操作系统直接运行在硬件之上,管理着底层物理硬件,这就构成了一个完整的计算机系统,也就是所谓的“物理机”。在虚拟环境中,虚拟机监控程序VMM抢占了操作系统的位置,变成了真实物理硬件的管理者,同时向上层的软件呈现出虚拟的硬件平台,“欺骗”着上层的操作系统。而此时操作系统运行在虚拟平台之上,仍然管理着它认为是“物理硬件”的虚拟硬件,俨然不知道下面发生了什么。
虚拟机可以看作是物理机的一种高效隔离的复制,上面的定义里蕴含了三层含义(同质、高效和资源受控),这也是一个虚拟机所具有的三个典型特征。虚拟机所必须具备的三个特点决定了不是在任何体系结构下都可以虚拟化的。给定一个系统,其对应的体系结构是否可虚拟化,就要看能否在该系统上虚拟化出具备上面三种特征的虚拟机。
为了进一步研究可虚拟化的条件,先从指令开始着手。大多数的现代计算机体系结构都有两个或两个以上的特权级,用来分隔系统软件和应用软件。系统中有一些操作和管理关键系统资源的指令会被定为特权指令,这些指令只有在最高特权级上能够正确执行。如果在非最高特权级上运行,特权指令会引发一个异常,处理器会陷入到最高特权级,交由系统软件来处理。在不同的运行级上,不仅指令的执行效果是不同的,而且也并不是每个特权指令都会引发异常。假如一个x86平台的用户违反了规范,在用户态修改EFLAGS寄存器的中断开关位,这一修改将不会产生任何效果,也不会引起异常陷入,而是会被硬件直接忽略掉。
在虚拟化的世界里,还有另一类指令被称为敏感指令,简而言之就是操作特权资源的指令,包括修改虚拟机的运行模式或者下面物理的状态;读写敏感的寄存器或是内存,例如时钟或者中断寄存器;访问存储保护系统、内存系统或是地址重定位系统以及所有的I/O指令。
为了VMM可以完全控制系统资源,它不允许直接执行虚拟机上操作系统(即客户机操作系统)的敏感指令。也就是说,敏感指令必须在VMM的监控审查下进行,或者经由VMM来完成。如果一个系统上所有敏感指令都是特权指令,则能够用一个简单的方法来实现一个虚拟环境:将VMM运行在系统的最高特权级上,而将客户机操作系统运行在非最高特权级上,当客户机操作系统因执行敏感指令(此时,也就是特权指令)而陷入到VMM时,VMM模拟执行引起异常的敏感指令,这种方法被称为“陷入再模拟”。
通过陷入再模拟敏感指令的执行来实现虚拟机的方法是有前提条件的:所有的敏感指令必须都是特权指令。否则,要么系统的控制信息会被虚拟机修改或访问,要么VMM会遗漏需要模拟的操作,影响虚拟化的正确性。如果一个体系结构上存在敏感指令不属于特权指令,那么其就存在虚拟化漏洞。
虽然虚拟化漏洞有可能存在,但是可以采用一些办法来填补或避免这些漏洞。最简单直接的方法是,所有虚拟化都采用模拟来实现,例如解释执行,就是取一条指令,模拟出这条指令的执行效果,再继续取下一条指令,那么就不存在所谓陷入不陷入的问题,从而避免虚拟化漏洞。这种方法不但能够适用于模拟与物理机相同体系结构的虚拟机,而且也能模拟不同体系结构的虚拟机。虽然这种方法保证了所有执行(包括敏感指令)执行受到VMM的监督和审查,但是它对每条指令不区别对待,其最大缺点很明显就是性能太差,是不符合虚拟机“高效”特点的,导致其性能下降为原来的十分之一甚至几十分之一。
既要填补虚拟化漏洞,又要保证虚拟化的性能,只能采取一些辅助的手段。或者直接在硬件层面填补虚拟化漏洞,又或者通过软件的办法避免虚拟机使用到无法陷入的敏感指令。这些方法都不仅保证了敏感指令的执行收到VMM的监督审查,而且保证了非敏感指令可以不经过VMM而直接执行,从而相比完全解释执行来说,性能得到极大的提高。
处理器虚拟化
处理器虚拟化是VMM中最核心的部分,因为访问内存或者I/O的指令本身就是敏感指令,所以内存虚拟化与I/O虚拟化都依赖于处理器虚拟化的正确实现。
指令的模拟
VMM运行在最高特权级,可以控制物理处理器上所有关键资源;而客户机操作系统运行在非最高特权级,所以其敏感指令会陷入到VMM中通过软件的方式进行模拟。从客户机操作系统的角度而言,无论一条指令是直接执行在物理服务器上,还是被VMM软件模拟,其期望的执行效果必须正确。所以,处理器虚拟化的关键在于正确模拟指令的行为。
介绍指令模拟之前,先理解三个概念:虚拟寄存器、上下文和虚拟处理器。
从某种程度上来说,物理处理器无非是包括了一些存放数据的物理寄存器,并且规定了使用这些寄存器的指令集,然后按照一段预先写好的指令流,在给定时间点使用给定的部分寄存器来完成某种目的。
在没有虚拟化的环境里,操作系统直接访问物理处理器,处在最高的特权级别,可以控制系统中所有关键资源,包括寄存器、内存和I/O外设等。但是,当VMM接管物理处理器后,昔日的操作系统称为客户机操作系统而降级到非最高特权级别上,这时,其试访问关键资源的指令就成为了敏感指令。VMM会通过各种手段,保证这些敏感指令的执行能够触发异常,从而陷入到VMM进行模拟,以防止对VMM自身的运行造成破坏。
所以,当客户机操作系统试图访问关键资源的时候,该请求并不会真正发生在物理寄存器上。相反,VMM会通过准确模拟物理处理器的行文,而将其访问定位到VMM为其设计与物理寄存器对应的“虚拟”的寄存器上。当然,从VMM实现来说,这样的虚拟寄存器往往是在内存中。
下图是一个具体的访问控制寄存器CR0的例子。
当处理取下一条指令MOV CR0,EAX后,发现特权级别不符合,则抛出异常,VMM截获这个异常之后模拟处理器的行为,读取EAX的内容并存放到虚拟的CR0中,由于虚拟的CR0存放在VMM为该虚拟机设计的内存区域里,因此该指令执行的结果并不会让物理的CR0的内容发生改变。等到下一次,当虚拟机试图读CR0时,处理器也会抛出异常,然后由VMM从虚拟的CR0而不是物理的CR0中返回内容给虚拟机。
在没有虚拟化的环境里,操作系统直接负责物理处理器管理,负责进程间调度和切换。但是,VMM接管物理处理器后,客户机操作系统没有管理物理处理器的权利,可以说此时它已经运行在VMM为之设计的虚拟处理器之上,管理虚拟处理器,并在虚拟处理器上负责该虚拟机内进程间调度和切换。而VMM管理物理处理器,负责虚拟处理器的调度和切换,以保证在给定时间内,每个虚拟处理器上的当前进程可以在物理处理器上运行一段时间。但是,不管何种调度切换,必然要涉及到保留现场,这个现场就是上下文状态,只不过前一种情况是进程上下文,后一种情况是虚拟器上下文。
进程上下文主要是与运算相关的寄存器状态,例如EIP寄存器指向进程当前执行的指令,ESP存放当前进程的堆栈指针等。当操作系统进行调度时,当前进程的上下文,即上述寄存器状态被保存在进程特定的内存区域中,而下一个进程的上下文被恢复到相应的寄存器中,从进程角度看,就好像从未被中断过一样。
虚拟处理器上下文比进程上下文更为复杂,因为客户机操作系统本身包含许多敏感指令,会试图访问和修改物理处理器上定义的所有寄存器,而这种访问和修改会被VMM重定位到虚拟处理器上。所以,对于虚拟处理器,其上下文包括了更多的系统寄存器,例如CR0、CR3、CR4和各种MSR等。当VMM在决定切换虚拟处理器的时候,为了让虚拟机看来好像从未被中断过一样,VMM需要考虑保存与恢复的上下文也更为复杂。
关于虚拟处理器。首先,从客户机操作系统来说,其在运行的处理器需要具备与其期望的物理处理器一致的功能和行为,这种期望的前提条件甚至可以允许客户机操作系统的修改,如VMM可以通过修改客户机操作系统的源代码,使客户机操作系统所期望的与VMM所呈现的功能集合一致。典型的期望包括:
- 指令集合与执行效果。
- 可用寄存器集合,包括通用寄存器以及各种系统寄存器。
- 运行模式,例如实模式、保护模式和64位长模式等。处理器的运行模式决定了指令执行的效果、寻址宽度与限制以及保护力度等。VMM必须正确模拟虚拟机期望的运行模式,否则会对虚拟机甚至是VMM自身的运行产生严重影响。
- 地址翻译系统,例如页表级数。
- 保护机制,例如分段和分页。
- 中断/异常机制,例如虚拟处理器必须能够正确模拟真实处理器的行为,在错误的执行条件下,为虚拟机注入一个虚拟的异常。
其次,从VMM的角度来书,虚拟处理器是其需要模拟完成的一组功能集合。虚拟处理器的功能可以由物理处理器和VMM共同完成。对于非敏感指令,物理处理器直接解码处理其请求,并将相关的效果直接反映到物理寄存器上;而对于敏感指令,VMM负责陷入再模拟,从程序的角度也就是一组数据结构与相关处理代码的集合。数据结构用于存储虚拟寄存器的内容,而相关处理代码负责按照物理处理器的行为将效果反映到虚拟寄存器上。
值得一提的是,VMM已经可以为虚拟机呈现出与实际物理机不一致的功能和行为。例如,虚拟处理器的个数,可以与物理处理器的个数不一致。在有多个物理处理器的平台上,VMM可以让虚拟机看到好像只有一个物理处理器(即一个虚拟处理器),而在只有一个物理处理器的平台上,VMM可以让虚拟机看到该凭条好像有多个物理处理器(即多个虚拟处理器),这种效果完全取决于用户对虚拟环境的配置,以及VMM自身的策略。
在处理器虚拟化中,不论是定义虚拟寄存器和虚拟处理器,还是利用上下文进行虚拟处理器调度切换,其宗旨都是让虚拟机里执行的敏感指令陷入下来之后,能被VMM模拟,而不要直接作用于真实硬件上。
当然,模拟的前提是能够陷入。VMM陷入是利用了处理器的保护机制,利用中断和异常来完成的。它有以下几种方式。
- 基于处理器保护机制触发的异常,例如敏感指令的执行。处理器会在执行敏感指令之前,检查其执行条件是否满足,例如当前特权级别、运行模式以及内存映射关系等。一旦任一条件不满足,VMM得到陷入然后进行处理。
- 虚拟机主动触发异常,也就是通常所说的陷阱。当条件满足时,处理器会在触发陷阱的指令执行完毕后,再抛出一个异常。虚拟机可以通过陷入指令来主动请求陷入到VMM中去。
- 异步中断,包括处理器内部的中断源和外部的设备中断源。这些中断源可以是周期性产生中断的时间源,也可以是根据设备状态产生中断的大多数外设。一旦信号到达处理器,处理器会强行中断当前指令,然后跳到VMM注册的中断服务程序,所以这也为VMM的陷入提供了一种途径。例如,VMM可以通过调度算法指定当前虚拟机运行的时间片长度,然后编程外部时钟源,确保时间片用完时触发中断,从而允许VMM进行下一次调度。
中断和异常的模拟以及注入
中断和异常机制是处理器提供给系统程序的重要功能,异常保证了系统程序对处理器关键资源的绝对控制,而中断提供了与外设之间更有效的一种交互方式。所以,VMM在实现处理器虚拟化时,必须正确模拟中断与异常的行为。
VMM对于异常的虚拟化需要完全遵照物理处理器对各种异常条件的定义,再根据虚拟处理器当时的内容,来判断是否需要模拟出一个虚拟的异常,并注入到虚拟环境中。
VMM通常会在硬件异常处理程序和指令模拟代码中进行异常虚拟化的检查。无论是哪一条路径,VMM需要区分两种原因:一是虚拟机自身对运行环境和上下文的设置违背了指令正确执行的条件;二是虚拟机运行在非最高特权级别,由于虚拟化的原因触发的异常。第二种情况是由于陷入再模拟的虚拟化方式造成的,并不是虚拟机本身的行为。而对于第一种情况的检查,VMM实际是在虚拟处理器的内容上进行的,因为它反映了虚拟机所期望的运行环境。错误的异常注入会让客户机操作系统做出错误的反应,后果无法预知。
物理中断的触发来自于特定的物理中断源,同样,虚拟中断的触发来自于虚拟设备的模拟程序,当设备模拟器发现虚拟设备状态满足中断产生的条件时,会将这个虚拟中断通知给中断控制器的模拟程序,例如模拟LAPIC。最后,VMM会在特定的时候检测虚拟中断控制器的模拟,例如LAPIC时钟、处理器间中断等;外部虚拟设备的模拟,例如8254、RTC、IDE、网卡和电源管理模块等;直接分配给虚拟机使用的真实设备的中断,通常来自于VMM的中断服务程序;自定义的中断类型。
当VMM决定向虚拟机注入一个中断或异常是,它需要严格模拟物理处理器的行为来改变客户指令流的路径,而且还要包括一些必需的上下文保护和恢复。VMM需要首先判断当前虚拟机的执行环境是否允许接收中断或是异常的注入,假如客户机操作系统正好通过RFLAGS,IF位禁止了中断的发生,这时,VMM就只能把中断事件暂时缓存起来,直到某时刻客户机操作系统重新允许了中断的发生,VMM才立即切入来模拟一个中断的注入。而当中断事件不能被及时注入时,VMM还要进一步考虑如下因素。
- 该中断类型是否允许丢失中断,如果允许,VMM则可以将其后到达的多个同类型中断合为一个事件;否则,VMM必须要跟踪所有后续到达的中断实例,在客户指令流重新允许中断时,将每一个缓存的中断一一注入。
- 该中断在阻塞期间是否被中断源取消,这决定了VMM是否会额外地注入一个已经被取消的假中断。
- 当一次阻塞的中断实例比较多时,VMM可能还需要考虑客户及操作系统能否处理短时间内大量同类型的中断注入,因为这在真实系统中可能并不出现。
在模拟中断或异常的注入时,VMM需要首先判断是否涉及到运行模式的切换。加入虚拟机可能运行在一个64位兼容模式,而其中断/异常处理函数运行在64位长模式,这时VMM就需要按照处理器的规定,将虚拟机的运行模式进行软件切换,对保存的客户上下文进行相应的修改。在可能的模式切换完成后,VMM还需要根据真实处理器在该模式下的中断注入过程,完整地进行软件模拟。例如,将必需的处理器状态(指令地址、段选择子等)复制压入当前模式下对应中断/异常服务程序的堆栈;到中断模拟逻辑去查找发生中断的向量号;根据该向量号来查找相关的中断/异常服务程序的入口地址;最后修改虚拟机的指令地址为上述入口地址,然后返回到虚拟机去执行等。
对称多处理器技术的模拟
在没有虚拟化的环境里,对称多处理器技术可以让操作系统拥有并控制多个物理处理器,它通过提供并发的计算资源和运算逻辑,允许上层操作系统同时调度多条基于不同计算目的的进程并发执行,从而有效地提高系统的吞吐率与性能。
当计算资源足够多时,VMM也可以考虑为虚拟机呈现出多个虚拟处理器,也就是客户对称多处理器虚拟化技术,也称客户SMP技术。这样,当这些虚拟处理器同时被调度在多个物理处理器上执行时,也可以有效地提高给定虚拟机的性能。虽然从VMM调度的角度来说,因为VMM仍然在物理处理器上基于一定的策略来管理多个虚拟处理器,客户SMP功能的加入并没有带来整体性能改观。但是,对于某个虚拟机来说,以前每个虚拟机处理器属于各自不同的虚拟机,而现在客户SMP功能引入后,某些具有相同属性的虚拟处理器可以隶属同一个虚拟机,从而使虚拟机因为拥有多个计算资源而可以让其虚拟机性能较其他虚拟机得到提高。
因此可以说,客户SMP中虚拟处理器的数目与实际物理处理器数目之前没有必然联系,也就是说,客户SMP中虚拟处理器的个数可以小于、等于或是大于实际物理处理器个数。当然,客户SMP功能引入后,VMM在虚拟环境的管理和责任发生了一些变化。
首先,VMM必须按照客户机操作系统期望的那样呈现客户SMP的存在,这样客户机操作系统才不会认为其运行在单一处理器上,才会试图初始化其他的虚拟处理器,并在其上运行调度程序。VMM可以是模拟一个现实中的接口,例如通过ACPI表来表述;也可以是自定义一个接口协议,只要客户机操作系统被修改来配合VMM即可。
其次,SMP的并发执行能力虽然带来了性能上的提升,但多个处理器竞争共享资源的情况也给软件实现带来了更多复杂性。为了保证SMP情况下多处理器访问共享资源的正确性,通常系统程序需要实现一条同步机制来协调处理器之间的步调,从而确保任何时候只有一个处理器才能对共享资源进行修改,并且在释放修改权之前,确保修改的效果能够被每个处理器察觉到。在客户SMP机制引入后,实际上VMM也面临着物理处理器之前(主机SMP)以及虚拟处理器之前(即客户机SMP)的同步问题。
- 对于发生在VMM自身代码之间的同步问题,由VMM负责协调物理处理器之间的步调来满足主机SMP的要求。
- 对于发生在同一个虚拟机内部,多个虚拟处理器间的同步问题,通常VMM并不需要参与,因为这是客户机操作系统自身的职责。VMM只需要在客户机操作系统发起某种特权操作,比如刷新页表时,正确模拟其效果即可。
- 对于VMM造成的虚拟处理器之前的同步问题,仍然需要VMM来负责处理。例如,VMM可能将N个虚拟处理器在M(M > N)个物理处理器之前进行迁移,客户机操作系统只知道自己有N个虚拟处理器,所以只会在这N个虚拟处理器的上下文内进行同步操作,但当VMM将这个N个虚拟处理器迁移到M个物理处理器上运行时,VMM就必须负责所以M个物理处理器上状态的同步。
最后,VMM对虚拟机管理模块也必须根据客户SMP的存在做相应的修改。例如,挂起命令要区分挂起虚拟处理器还是挂起虚拟机,当挂起某个虚拟机就必然挂起该虚拟机内部所有指令流的执行。
通常,对称多处理器技术定义有标准的一套初始化过程。在没有虚拟化的环境里,BIOS负责选取BSP(Bootstrap Processor, 引导处理器)与AP(Application Processor, 应用处理器),把所有处理器都初始化到某种状态后,BIOS在BSP上通过启动加载程序(Boot Loader)跳转至操作系统的初始代码,同时所有的AP处于某种等待初始化硬件信号的状态。接下来,操作系统会在初始化到某个时刻时,发出某种初始化硬件信号给所有的AP,并提供一段特定的启动代码,AP在收到初始化硬件信号后,就会跳转到操作系统指定的启动代码中继续执行。通过这样一种方式,操作系统最终就成功地按自己的方式初始化了所有的处理器,最后在每个处理器上独立地运行调度程序。
那么,在虚拟环境里,客户SMP功能被引入后初始化过程是怎样的,注意此时VMM已经启动运行起来,而客户机操作系统正处于初始化阶段,VMM选择第一个虚拟处理器作为BSP,其他虚拟处理器为AP,把所有虚拟处理器都初始化到某种状态。这里又分为两种情况:如果客户机操作系统是不能修改的,而它又期望看到虚拟处理器与物理处理器加电重设后一样的状态,VMM就必须按照软件开发手册上对于处理器加电重设状态的描述,设置虚拟处理器的寄存器状态,包括虚拟寄存器和虚拟运行模式等;如果客户机操作系统可以修改,VMM就可以使用一套自定义的协议而不必依照规范定义的那样,例如直接跳过实模式把虚拟处理器初始化为保护模式。接下来,当启动代码初始化到某个时刻时,AP需要收到某种初始化信号被唤醒。这里也分为两种情况:如果客户机操作系统不能被修改,则VMM负责截获客户机操作系统发出的INIT-SIPI-SIPI序列,唤醒其他虚拟的AP;如果客户机操作系统可以被修改,VMM也可以自定义一套简单的唤醒机制。
内存虚拟化
物理内存存在两个主要基本认识:物理地址从0开始和内存地址连续性。内存虚拟化的产生,主要源于VMM与客户机操作系统在物理内存的认识上存在冲突,造成物理内存的真正拥有者——VMM,必须对客户机操作系统访问的内存进行一定程度上的虚拟化。内存虚拟化,即满足了客户机操作系统对内存和地址空间的特定认识,也可以更好地在虚拟机之间、虚拟机与VMM之前进行隔离,防止某个虚拟机内部的活动影响到其他的虚拟机甚至是VMM本身,从而造成安全上的漏洞。
指令对于内存的访问都是通过处理器来转发的,首先处理器会将解码后的请求发送到系统总线上,然后由芯片组来负责进一步转发。为了唯一标识,处理器采用统一编址的方式将物理内存映射成为一个地址空间——物理地址空间。
平时,我们把一根根内存条插到主板上的内存插槽中,每根内存条都需要被映射到物理地址空间中某个位置。一般来说,每根内存插槽在物理地址空间的起始地址可以在主板制造时就被固定下来,也可以通过某种方式由BIOS加电后自动配置。一旦内存插槽的起始地址就被固定下来,这根内存条上每个字节的物理地址就相应地确定下来了。总的来看,一根根内存条形成一个连续的物理地址空间,而且这个物理地址空间一定是从0开始的。
例如,有4个内存插槽的主板,每个插槽插上4G的内存条,如果这4个插槽的起始地址分别固定为0x000000000、0x100000000、0x200000000、0x300000000,那么它们上面的物理内存就被映射成0x000000000——0xFFFFFFFF、0x100000000——0x1FFFFFFFF、0x200000000——0x2FFFFFFFF、0x300000000——0x1FFFFFFFFF这4段。总的来说,这4条内存条组成16GB的内存,而且这16GB的内存是0开始的连续空间,4根内存条上每个字节都会对应一个唯一的物理地址。处理器访问任何一个字节就是通过请求一个物理地址,芯片组收到处理器发出的内存访问请求后,就会检测内部维护的物理地址空间的分配表,当发现目标地址落在0x000000000——0x1FFFFFFFFF范围时,处理器就会进一步把请求转发给内存控制器。
在x86上,Linux内核可执行文件头里定义了每个段的大小、期望在物理地址空间中被加载的位置即1MB,以及加载后执行第一条指令的地址等,这些信息在编译链接阶段就确定下来了。由于加载的位置时1MB,那么对于后面代码,其访问的段都是基于1MB这个起始地址的,这也是在编译链接阶段就确定下来了的。通常,在加载内核时,启动加载程序(Boot Loader)就会通过对该文件格式的分析,将相应的段复制到期望的位置,然后跳转到内核文件指定的入口点。而系统所做的,必须保证在该指定位置存在可用内存。如果物理地址空间不是从0开始的,Boot Loader将会因指定位置找不到可用内存而拒绝加载内核,即使是把内核加载到内存中了,由于内核代码在访问段时也会自身产生错误而造成整个系统的崩溃。
除此之外,现实中的操作系统基本对内存连续性存在一定程度的依赖性,如DMA。DMA的目的就是允许设备绕过处理器来直接访问物理内存,从而保证I/O处理的高效。
上图是一个简化的例子,现实中的设备在DMA的逻辑上要复杂得多。左边的设备使用了一种最直接的方式,即驱动程序提供DMA的目标内存地址0x100000以及大小1MB,然后设备顺序访问从0x100000到0x200000的内存。很容易看到,当一个页面大小小于1MB时,就需要请求几个在物理上连续的内存页面,以满足设备上顺序访问内存的需求。而右边的设备则使用了一种更加灵活的方式,叫作分散-聚合(Scatter-Gather),它允许驱动程序一次提供多个物理上不连续的内存段,设备通过相关信息来离散地访问这些不连续的目标内存。
在实际设备中,这两种模式都非常普遍,而且即使在后一种模式中,设备允许的离散块是有限的,为了支持更大的DMA区域,驱动程序仍然会在每一个离散块中分配多个连续的内存页面,这就意味着驱动程序必须能够从操作系统中分配到足够多连续的空闲内存页来满足DMA的要求。
除了满足上述设备DMA的需求外,操作系统还在其他方面对内存连续性的要求。例如,某些情况下物理连续的页面可以简化程序设计,并且带来性能上的提升。又比如,操作系统可以在处理器的帮助下,使用大页面映射的方式加速对一开连续内存页面的访问。
总而言之,在没有虚拟化的情况下,操作系统在对内存的使用与管理上已经达成以下两点认识。
- 内存都是从物理地址0开始的。
- 内存都是连续的,或者说至少在一些大的粒度(如256MB)上连续。
内存虚拟化面临的问题是:物理内存要被多个客户机操作系统同时使用,但物理内存只有一份,物理起始地址0也显然只有一个,无法同时满足所有客户机操作系统从0开始的要求;由于使用内存分区方式,把物理内存分给多个客户机操作系统使用,客户机操作系统的内存连续性要求虽然能得到解决,但是内存的使用效率非常不灵活。
在内存虚拟化中,VMM负责管理和分配每个虚拟机的物理内存,客户机操作系统看到的是一个虚构的客户机物理地址空间,其指令目标地址也是一个客户机物理地址。这样的地址在无虚拟化的情况下,其实就是实际物理地址。但是,在有虚拟化的情况下,这样的地址是不能直接发送到系统总线上去的,需要VMM负责将客户机物理地址首先转换成一个实际物理地址后,再交由物理处理器来执行。
为了更有效地利用空闲的物理内存,尤其是系统长期运行后产生的碎片,VMM通常会以比较小的粒度(如4KB)进行分配,这就造成了给定一个虚拟机的物理内存实际上是不连续的问题,其具体位置完全取决于VMM的内存分配算法。
由于引入了客户机物理地址空间,内存虚拟化就主要处理以下两个方面的问题。
- 给定一个虚拟机,维护客户机物理地址到宿主机物理地址之间的映射关系。
- 截获虚拟机对客户机物理地址的访问,并根据所记录的映射关系,将其转换成宿主机物理地址。
第一个问题相对比较简单,因为这只是一个数据结构的映射问题。在实现过程中,客户机操作系统采用客户页面维护可该虚拟机里进程所使用的虚拟地址到客户机物理地址的动态映射关系,用一个简单的公式表示就是GPA = f1(GVA)。这里,GVA代表客户机虚拟地址,GPA代表客户机物理地址。而VMM负责维护客户机物理地址到宿主机物理地址之间的动态映射关系,用一个简单的公式表示就是HPA = f2(GPA)。这里,HPA代表宿主机物理地址。虚拟机里一个进程所使用的客户机虚拟地址要变成物理处理器可以执行的宿主机物理地址,需要经过两层转换,即HPA = f2(f1(GPA))。
VMM内存虚拟化任务就是跟踪客户页面,当其发生变化时,及时地切入,构造一个有效的客户机虚拟地址到宿主机物理地址间的映射关系,加到物理处理器所遍历的真实页表上。
第二个问题从实现上来说更加复杂和具有挑战性,也是衡量一个虚拟机的性能最重要的方面。再者,地址转换一定要发生在物理处理器处理目标指令之前,否则一旦客户机物理地址被发送到系统总线上,那会对整个虚拟环境,包括其他虚拟机以及VMM自身,造成严重的破坏和巨大的漏洞。
一个最简单的方法就是设法让虚拟机对客户机物理地址空间的每一次访问都触发异常,然后由VMM来查询地址转换表模拟其访问。这种方法的完备性和正确性没有任何问题,但性能上绝对是最差的。
内存虚拟化除了能满足客户机操作系统对内存的两点认识之外,还实现了整个系统的安全隔离,包括虚拟机与虚拟机之间,以及虚拟机与VMM之间。
- VMM通过处理器硬件功能使得客户机操作系统与之完全运行在不同的地址空间里,或者通过段限制来限制客户机操作系统所能看见的空间大小,以保证VMM自身的安全性,从而防止虚拟机触及VMM自身的运行状态。
- VMM通过特殊的权限验证机制使得客户机操作系统局限在给定的地址空间里,以保证一个虚拟机只能访问分配给它的内存页,从而避免存在设计上的漏洞让某个虚拟机可以访问目标内存的特点,恶意访问设备的DMA目标寄存器,进而通过设备越权访问所有物理内存。
I/O虚拟化
现实中的外设资源也是有限的,为了满足多个客户机操作系统的的需求,VMM必须通过I/O虚拟化的方式来复用有限的外设资源。VMM截获客户机操作系统对设备的访问请求,然后通过软件的方式来模拟真实设备的效果。模拟软件本身作为物理驱动程序众多客户端中的一个,从而有效地实现了物理资源的复用。由于从处理器的角度看,外设是通过一组I/O资源(端口I/O或者是MMIO)来进行访问的,所以设备相关的虚拟化又被称为I/O虚拟化。
概述
首先分析一下以往没有虚拟化的情形。给定一个外设,定义有自己的一套共软件访问的接口,这些接口的属性可能是单向的,也可能是双向的。操作系统含有外设的驱动程序,它们接收来自其他模块(如用户进程)的请求,然后按照外设规定好的驱动方式驱动外设完成特定的任务。驱动程序并不关系外设内部的逻辑电路是如何完成的,只要驱动程序按照定义好的接口使用外设,外设总会通过其内部逻辑电路完成期望的效果。由于处理器在计算机的核心地位,因此外设的访问接口最终也会被映射到处理器所能认识的地址空间或者其他资源中。这样,当驱动程序通过指令的方式访问外设接口时,处理器才能正确识别目标对象,然后将相关请求发送到系统总线上,最终由芯片组转发给目标外设。
- I/O端口寄存器。它被映射到I/O地址空间中,这个空间由特殊的指令如IN/OUT访问。如果指令流处于最高特权级别,整个64KB的I/O地址空间都可以自由访问,驱动程序就是这样一个例子;当指令流运行在其他特权级别时,只有I/O位图中允许的端口才可以访问。
- MMIO(Memory-mapped I/O,内存映射)寄存器。它被映射到物理地址空间中,通过页表的方式来控制访问权限。
- 中断。由于外设的物理构造,速度远低于处理器,往往需要异步事件通知的方式来完成延后的操作,这个通知机制通常通过中断模块实现。每个允许发生中断的外设会在处理器的中断向量空间中分配一个序号,中断模块发出中断消息,通过芯片组中的中断控制器通知给处理器,最后处理器会中断当前指令流,跳到中断描述符表IDT中对应该向量的中断服务程序执行。
I/O端口、MMIO与中断模块组成了一个典型外设呈现给软件的基本资源。上图中还额外标注了外设中一个特殊的模块——DMA模块。DMA提供给设备不经处理器而直接访问内存的方式,从而特别适用于大批量数据的批量传输。从访问方式来说,DMA模块被映射在I/O端口或者是MMIO中。
在虚拟环境里,I/O面临的问题是:现实中外设资源是有限的,为了满足多个客户机操作系统对外设访问的需求,VMM必须通过I/O虚拟化的方式复用有限的外设资源。在面临这种问题的情况下,VMM所要做的还是模拟,即截获客户机操作系统对设备的访问请求,然后通过软件的方式模拟真实物理设备的效果。
I/O虚拟化并不需要完整地虚拟化出所有外设的所有接口,究竟怎么样完全取决于设备与VMM的策略以及客户机操作系统的需求。
- 虚拟芯片组。基于VMM实现上的考虑,这个芯片组还可以承担ACPI电源管理相关的一些功能。
- 虚拟PCI总线布局,主要是通过虚拟化PCI配置空间,为客户机操作系统呈现虚拟的或是直接分配使用的设备。
- 虚拟系统设备,例如PIC、IO-APIC、PIT和RTC等。
- 虚拟基本输入输出设备,例如显卡、网卡和硬盘等。
虚拟完毕后,只要客户机操作系统中有驱动程序遵守该虚拟设备的接口定义,它就可以被客户机操作系统所使用。
设备发现
设备发现就是要让VMM提供一种方式,来让客户机操作系统发现虚拟设备,这样客户机操作系统才能加载相关的驱动程序,这是I/O虚拟化的第一步。设备发现取决于被虚拟的类型。
- 模拟一个所处物理总线的设备,这其中又包含以下两种类型。
- 模拟一个所处总线类型是不可枚举的物理设备,而且该设备本身所属的资源是硬编码固定下来的。这类设备典型的例子就是ISA设备、PS/2键盘、鼠标、RTC以及传统IDE控制器等。对于这类设备,驱动程序可能会通过特定的方式来检测设备是否存在,例如读取特定端口的状态信息。所以,只要VMM在给定端口进行了正确的模拟,客户机操作系统就能够承购检测到虚拟设备的存在。
- 模拟一个所处总线是可枚举的物理设备,而且相关设备资源是软件可配置的。这类设备的典型例子就是PCI设备。由于PCI总线同PCI配置空间定义了一套完备的设备发现方式,并且允许系统软件(BIOS或操作系统)通过PCI配置空间的一些字段对给定PCI设备进行资源的配置,例如允许或禁止I/O端口和MMIO,设置I/O和MMIO的起始地址等。所以,VMM仅靠模拟设备自身的逻辑是不够的,它必须进一步模拟PCI总线的行为,包括拓扑关系和设备特定的配置空间内容,以便让客户机操作系统发现这类虚拟设备。
- 模拟一个完全虚拟的设备,例如前面介绍的FE/BE模型。在这种情况下,因为没有一个现实中的规范与之对应,这种虚拟设备所处的总线类型将完全由VMM自行决定,VMM可以选择将虚拟设备挂在PCI总线上,也可以完全自定义一套虚拟总线协议。因此,当遇到相对复杂的PCI总线需要模拟时,VMM完全可以自定义并使用一套简化的、适用于该类型虚拟设备的总线,避免模拟的复杂性。当然,客户机操作系统里也必须加载一个特殊的总线驱动才行。因此,使用原来PCI总线的好处是兼容性,客户机操作系统可以重用已有的总线驱动来发现虚拟设备。而引入新的总协议带来的问题就是维护性,而且,为了在不同的VMM上运行,同一个客户机操作系统可能需要加载许多不同的总线协议驱动程序。
访问截获
现在,虚拟设备已经被客户机操作系统发现了,客户机操作系统中的驱动程序就会按照接口定义访问这个虚拟设备。VMM的问题又来了,它不仅需要知道那个虚拟设备相关的接口资源,而且它还要找到有效的办法来截获客户机操作系统对虚拟设备的访问,并进行模拟。可以毫不犹豫地说,关键点就在于处理器虚拟化。
对于一个非直接分配给客户机操作系统使用的设备,假如该设备可以具有端口I/O资源。已知处理器对于端口I/O资源的控制在于指令流所处的特权级别和相关I/O位图。由于客户机操作系统被降级运行在一个非特权的环境里,客户机操作系统是否能够访问给定I/O端口就玩去由I/O位图来决定。自然地,VMM可以把设备的所有端口I/O从I/O位图中关闭,这样,当客户指令流在访问该I/O端口时,物理处理器就会及时地抛出一个保护异常,接着,VMM就可以获得异常原因,然后将请求发送给设备模拟器进行模拟。相反,对于一个直接分配给客户机操作系统使用的设备,VMM可以把该设备所属端口I/O从I/O位图中打开,这样,处理器就会把访问发送到系统总线,最终到达目标物理设备而不被模拟。
对于一个非直接分配给客户机操作系统使用的设备,假如设备可以提供MMIO资源。已知MMIO本身也是物理地址空间的一部分,而物理地址空间的访问控制是通过页表来控制的,因此在物理处理器遍历的真实页表里,VMM只要把映射到该MMIO的页表项设为无效,当客户指令试图再访问目标地址时,物理处理器就会抛出一个缺页异常,接着VMM遍历客户表项,就可以发现设备所属的MMIO资源,然后将请求发送给设备模拟器进行模拟。相反,对于一个直接分配给客户机操作系统使用的设备,VMM只需要按照客户页表的设置打开真实页表的映射即可,这样客户机操作系统对该设备的访问也不再被模拟。
对于一个非直接分配给客户机操作系统使用的设备,假如该设备可以产生中断。那么VMM只要提供一种机制,供设备模拟器在接收到物理中断并需要触发中断时,可以通知到虚拟中断逻辑,然后由虚拟中断逻辑模拟一个虚拟中断的注入;相反,对于直接分配给客户机操作系统的设备,VMM物理中断处理器函数在接收到物理中断后,辨认出中断源属于某个客户机,然后通知该客户机的虚拟中断逻辑。
假如某设备可以提供DMA或类似的共享内存机制,那么,已知DMA允许设备绕过处理器直接访问目标内存,而若客户驱动程序是未经修改的,则设备模拟器接收到的DMA目标地址是客户机物理地址。因此,VMM只要提供一种机制,让设备模拟器可以了解各种地址之间转换关系,从而可以把客户机物理地址映射成自己的虚拟地址,就能真正做到对DMA目标地址的访问了。
设备模拟
至于虚拟化方式,虚拟现实可以与虚拟现实设备具有完全一样的接口定义,从而允许客户机操作系统中原油驱动程序无须修改就能驱动这个虚拟设备。这时,VMM的设备模拟器往往需要仔细研究现实设备的接口定义和内部设计规范,然后以软件的方式模拟真实的逻辑电路来满足每个接口的定义和效果,例如PS/2键盘、鼠标等。在这种情况下,现实设备具备那些资源,设备模拟器就需要呈现出同样的资源。
既然无论如何VMM需要在客户机操作系统中提供一个特定的驱动程序,那么还可以把这个驱动程序进一步简化,并称客户机操作系统中的驱动程序为前端(Front-End,FE)设备驱动,而VMM中的驱动程序为后端(Back-End,BE)设备驱动。简化就是前端程序将来在自于其他驱动模块的请求通过客户机之间的特殊通信机制直接发送给后端程序,而后端程序在处理完请求后再发回通知给前者。如下如所示。
与传统设备驱动程序流程比较,传统设备驱动程序为了完成一次操作要涉及到多个寄存器的操作,使得VMM要截获每个寄存器访问并进行相应的模拟,也就导致多次上下文切换。但是,这种方式是基于请求/事务的,能在很大程度上减少上下文切换的频率,提供更大的优化空间,这种方式可以看做是上一种方式的衍生。
如果直接将物理设备分配给某个客户机操作系统,由客户机操作系统直接访问目标设备,VMM不需要为这种方式提供模拟,客户机操作系统中原有的驱动程序也可以无缝地操作目标设备,这种I/O虚拟化的方式从性能上来说是最优的。但是,这种虚拟方式仍然会受到最大可用资源的限制。目前与此相关的技术有IOMMU,Intel VT-d和PCI-SIG的SR-IOV等。
归纳一下以上三种I/O虚拟化方式,如下图。
I/O虚拟化的方式是非常灵活的,完全基于VMM本身对于所构建的虚拟环境设计。而所虚拟出来的设备可以与某种真实物理设备一致,也可以截然不同,在这种情况下,VMM需要在客户机操作系统中显式地安装特殊的驱动才能使之正常工作。在性能成为关键瓶颈时,VMM甚至可以把物理设备控制权交给某个客户机操作系统。
设备共享
VMM可以选择地虚拟化一些不同的设备,其中有些设备可以被设备模拟器用软件的方式完全模拟出来而不用接触实际物理设备,如CMOS。有些设备可能需要设备模拟器进一步去请求物理驱动的帮助,也就是需要利用物理外设资源。一般输入输出类设备就属于后一类设备,例如鼠标、键盘、显卡、硬盘和网卡等。这些设备都涉及到从真实的物理外设获取输入,或者需要往真实的物理外设上输出内容。
设备模拟器通常运行在一个I/O特权环境中,这样的I/O特权环境中有驱动物理外设的物理驱动程序。在这种情况下,相关的设备模拟器本身是作为物理驱动程序的一个客户而存在,例如一个用户进程。I/O特权环境中的大多数物理驱动都是可以同时接受多个客户或是进程的请求,从而达到物理资源的复用。同样,每个虚拟机都有自己专属的设备模拟逻辑,也就是I/O特权环境中存在一个相对应的用户进程。通过这种方式,I/O虚拟化就有效地将物理资源在多个虚拟环境中复用起来。如下图所示。
VMM的功能和组成
VMM的主要职责是构建符合同质、高效、资源统一这三个特点的虚拟机。VMM的主要功能就是基于物理资源创建相应的虚拟资源,组成虚拟机,为客户机操作系统提供虚拟的平台。
顺理成章,VMM基本上可以分为两部分:虚拟环境的管理和物理资源的管理。前一部分是所有VMM产品所需要提供的基本功能,而后一部分实现结构的差异,其存在也是各有差异的。
虚拟环境的管理
虚拟资源
通过截获客户机操作系统对处理器、内存和外设等资源的访问,来构建一个虚拟环境。在这个环境中,客户机操作系统认为自己运行在一台计算机上,并唯一地拥有这台虚拟机器上的所有资源,所以VMM需要提供如下基本模块。
- 处理器虚拟化模块。 为虚拟机提供虚拟处理器。
- 内存虚拟化模块。为虚拟机提供虚拟内存。
- 设备虚拟化模块。为虚拟机提供虚拟I/O设备。
虚拟环境的调度
既然VMM可以同时构建多个虚拟环境,从而允许多个客户机操作系统并发执行,那么随之而来VMM必须实现一套策略来有效地调度。
VMM的调度程序和操作系统的调度程序类似。操作洗头工调度程序的调度单位是进程/线程,VMM调度程序的调度单位是虚拟处理器。当虚拟处理器被调度到时,VMM调度程序负责将虚拟机处理器上下文装载到物理处理器上,然后虚拟处理器所对应的客户机指令开始真正被执行。当时间片用完或者虚拟处理器主动让出,调度程序会被触发。调度程序根据调度策略,挑选下一个虚拟处理器继续运行。
与操作系统一样,VMM的调度策略可以有多种,例如平均分配时间片来进行调度,或者按照虚拟机的权重来分配时间片进行调度等。
虚拟机间通信机制
与操作系统中进程间通信机制类似,虚拟环境下也存在虚拟机之间通信机制。虚拟机间通信机制的作用,顾名思义,就是为虚拟机提供相互之间通信的手段。在某些情况下,虚拟机之间需要互相通信来完成特定功能。举例来说,类虚拟化I/O中是基于事务的模型,一个I/O事务需要特权虚拟机和正常虚拟机共同合作完成,中间就会有大量用到虚拟机间通信。
虚拟机间通信机制从实现上来说可以多种多样。通常来说,VMM实现虚拟机间的通信机制,向虚拟机提供相应的API。虚拟机的客户机操作系统通过调用这些API与其他虚拟机进行通信。这些API可以是事件通知,也可以是内存共享等。在虚拟机间通信机制实现上,严格的安全权限检查也是必需的,否则虚拟机之间的隔离就会收到影响。另外,VMM除了提供虚拟机之间通信的API外,也提供虚拟机与VMM之间交互的API。
虚拟环境的管理接口
虚拟机的管理功能要和非常重要。可管理性是用户挑选虚拟化产品的重要指标之一。实现上来说,虚拟机管理功能由上层的管理程序和VMM提供的管理接口组成。VMM需要提供一组完备的管理接口,来支持虚拟环境的创建、删除、暂停、查询和迁移等功能。上层的管理程序则通过调用VMM提供的管理接口,为用户提供管理界面。
物理资源的管理
与操作系统一样,VMM本身也承担了全部或者部分物理资源的管理工作。
处理器管理
包括系统启动时检测并获取所有的处理器;对买个处理器进行初始化,如设置运行模式、设置页表、设置中断处理函数等;将所有的处理器纳入调度序列,由调度程序对处理器进行调度。有些VMM还支持对物理处理器的热插拔,当有处理器插入时,VMM得到通知,初始化后将其纳入管理队列当中;当处理器拔出时,VMM同样得到通知,将该处理器上的任务迁移到其他处理器上,并将其从管理队列中删除。有些VMM还具有高可靠性的支持,当收到处理器失效通知时,如MCA(Machine Check Abort),VMM将其做热拔出处理。
内存管理
包括系统启动时VMM检测并且获得所有内存;对获得内存进行初始化,包括分页并设置页表等;提供内存分配的接口,以便VMM的其他模块能够获得/释放内存;给虚拟机分配内存,并且维护虚拟机物理地址与实际物理地址的映射关系,以供VMM的其他模块查询使用。
中断管理
VMM负责初始化并设置中断相关的资源,如处理器中断向量表、Local APIC和中断控制器(I/O APIC、8259 PIC)。当中断发生后,VMM是接收者,它会根据中断的来源,或者直接处理,或者转发到相关特权虚拟机来处理。
系统时间维护
VMM拥有和时间相关的硬件资源,因此VMM负责维护系统时间,并且向各个虚拟机提供虚拟化时间。
设备管理
在Hypervisor模型下,所有的外设都属于VMM,因此,VMM需要包含所有设备的驱动程序来管理这些设备。在混合模式下,大部分的外部设备属于特权客户机操作系统,由特权客户机操作系统的驱动程序来管理这些外设。VMM也拥有少部分的设备,如用于调试的串口,因此也需要这些设备的驱动程序。
其他模块
除了虚拟环境和物理资源的管理功能外,通常VMM还会包括以下功能模块。
软件定时器
软件定时器为VMM的其他模块提供了一种方法,使其能够在未来指定的某个时刻执行指定的动作。软件定时器通常是通过时钟中断处理函数来实现的,在VMM内部被广泛应用,如系统时间的维护。
多处理器同步原语
与操作系统一样,当多处理器共享同一个资源时,VMM需要提供同步原语来同步多处理器的读写访问,保证资源的正确性。
调试手段
调试手段(包括系统级别和虚拟化境特定)对于VMM的开发不可或缺。printk是最简单有时也是最有效的调试手段。有些VMM也会开发出类似gdb的调试工具。虚拟机还为客户机操作系统的调试提供了极好的环境,由于VMM能够控制客户机操作系统的运行,极大地方便了用户的调试。
性能采集与分析工具
VMM通常也会提供profiling工具,用于性能数据的采集和分析。这些功能能够采集VMM全局的性能数据,也能够采集针对某个虚拟机的性能数据。
安全机制
从基本功能上来说,VMM需要保证各个虚拟机之间,以及虚拟机与VMM之间是隔离的,虚拟机上运行的恶意代码只能影响该虚拟机本身,不能影响VMM和其他虚拟机。此外,虚拟化技术的出现,为安全提供了新的平台。
电源管理
与操作系统类似,VMM也支持电源管理,包括处理器电源管理、睡眠状态电源管理等。
VMM的分类
按虚拟平台分类
根据VMM所提供的虚拟平台类型可以将VMM分成两类:第一类VMM虚拟的是现实存在的平台,并且在客户机操作系统来看,虚拟的平台和现实的平台是一样的,客户机操作系统察觉不到是运行在一个虚拟平台上。这样的虚拟化平台可以运行现有的操作系统,无须对操作系统进行任何修改,因此这种方式被称为完全虚拟化(Full Virtualization)。第二类VMM虚拟的平台是现实中不存在的,而是经过VMM重新定义的,这样的虚拟凭条需要对所运行的客户机操作系统进行或多或少的修改使之适应虚拟环境,因此客户机操作系统知道其运行在虚拟平台上,并且会去主动适应。这种方式被称为类虚拟化(Para-Virtualization)。
完全虚拟化
在客户机操作系统看来,完全虚拟化的虚拟平台和现实的平台是一样的,客户机操作系统无须做任何修改就可以运行。这就意味着客户机操作系统会像操作正常的处理器、内存、I/O设备一样来操作虚拟处理器、虚拟内存和虚拟I/O设备。从实现的角度来看,VMM需要能够并且正确处理客户机所有可能的行为。进一步说,客户机的行为是通过指令反映出来的,因此VMM需要能够正确处理所有可能的指令。对于完全虚拟化来说,所有可能的指令是指所虚拟的处理器其手册规范上定义的所有命令。
在实现方式上,以x86架构为例,完全虚拟化经历了两个阶段:软件辅助的完全虚拟化和硬件辅助的完全虚拟化。
软件辅助的完全虚拟化
在x86虚拟化技术的早期,x86体系没有在硬件层次上对虚拟化提供支持,因此完全虚拟化只能通过软件实现。一个典型的做法是优先级压缩(Ring Compression)和二进制代码翻译(Binary Translation)相结合。
优先级压缩的原理是:由于VMM和客户机运行在不同特权级上,对应到x86架构上,通常是VMM运行在Ring 0,客户机操作系统内核运行在Ring 1,客户机操作系统应用程序运行在Ring 3。当客户机操作系统内核执行相关特权指令时,由于处在非特权的Ring 1,因此通常会触发异常,VMM截获该特权指令并进行虚拟化。Ring Compression能够正确处理大部分特权指令,但是由于x86指令体系在设计之初并没有考虑到虚拟化,因此有些指令还是不能通过Ring Compression正常处理,即在Ring 1中做特权操作的时候却没有触发异常,从而VMM不能截获并做相应处理。
二进制代码翻译方法因此被引入来处理这些对虚拟化不友好的指令。二进制翻译的思想也很简单,就是通过扫描并修改客户机的二进制代码,将难以虚拟化的指令转化为支持虚拟化的指令。VMM通常会对操作系统的而今中代码进行扫描,一旦发现需要处理的指令,就将其翻译成支持虚拟化的指令块(Cache Block)。这些指令块可以与VMM合作访问受限的虚拟资源,或者显式地触发吟唱让VMM进一步处理。此外,由于该技术可以修改客户机的二进制代码,因此也被广泛应用于性能优化,即将某些造成性能瓶颈的指令替换成更加高效的指令来提高性能。
优先级压缩和二进制代码翻译技术虽然能够实现完全虚拟化,但是这种打补丁的方式很难在架构上保证其完整性,因此x86厂商在硬件上加入了对虚拟化的支持,从而在硬件架构上实现了虚拟化。
硬件辅助完全虚拟化
操作系统作为硬件之上的最后一层系统软件,如果硬件本身加入足够的虚拟化功能,就可以截获操作系统对敏感指令的执行或者对敏感资源的访问,从而通过异常的方式报告给VMM,这样就解决了虚拟化的问题。Intel的VT-x技术是这一方向的代表。VT-x技术在技术处理器上引入了一个新的执行模式用于运行虚拟机。当虚拟机执行在这个特殊模式中时,它仍然面对的是一套完成的处理器寄存器集合和执行环境,只是任何特权操作都会被处理器截获并报告给VMM。VMM本身运行在正常模式下,在接收到处理器的报告后,通过对目标指令的解码,找到对应的虚拟化模块进行模拟,并把最终的效果反应在特殊模式下的环境中。
硬件虚拟化是一种完备的虚拟化方法,因为内存和外设的访问本身也是由指令来承载,对处理器指令级别的截获就意味着VMM可以模拟一个与真实主机完全一样的环境。在这个环境中,任何操作系统只要能够在现实中的等同主机上运行,也就可以在这个虚拟机环境中无缝地运行。
类虚拟化
类虚拟化是通过在源码级别修改指令以回避虚拟化漏洞的方式来使VMM能够对物理资源实现虚拟化。对于x86上存在的那一虚拟化的指令,完全虚拟化通过二进制代码翻译(Binary Translation)在二进制代码级别上避免虚拟化漏洞,类虚拟化采取的是另一种思路,即修改操作系统内核的代码(即API级),使得操作系统内核完全避免这些难以虚拟化的指令。
操作系统通常会使用到处理器提供的全部功能,例如特权级别、地址空间和控制寄存器等。类虚拟化首先需要解决的问题就是如何插入VMM。典型的做法就是修改操作系统的处理器的相关代码,让操作系统主动让出特权级别,而运行在次一级特权级上。这样,当操作系统视图去执行特权指令时,保护异常被触发,从而提供截获点供VMM来模拟。
既然内核diamante已经需要修改,类虚拟化进一步可以被用于优化I/O。也就是说,类虚拟化不是去模拟真实世界中的设备,因为太多的寄存器模拟会降低性能。相反,类虚拟化可以自定义出高度优化的I/O协议。这种I/O协议完全基于事务,可以达到近似物理机的速度。
按VMM实现结构分类
Hypervisor模型
在Hypervisor模型中,VMM首先可以被看作是一个完备的操作系统,不过和传统操作系统不同的是,VMM是为虚拟化而设计的,因此还具备虚拟化功能。从架构上来看,首先,所有的物理资源如处理器、内存和I/O设备等等都归VMM所有,因此,VMM承担着管理物理资源的责任;其次,VMM需要向上提供虚拟机用于客户机操作系统,因此,VMM还负责虚拟环境的创建和管理。
下图展示了Hypervisor模型的架构,其中处理器管理代码(Processor,P)负责物理处理器的管理和虚拟化,内存管理代码(Memnory, M)负责物理内存的管理和虚拟化,设备模型(Device Model,DM)负责I/O设备的虚拟化,因此,设备驱动也是VMM的一部分。此外,处理器管理代码、内存管理代码和设备模型也是VMM的一部分。
在Hypervisor模型中,由于VMM同时具备物理资源的管理功能和虚拟化功能,因此,物理资源虚拟化化的效率会更高一些。在安全方面,虚拟机的安全只依赖于VMM的安全,不会像下面将会提到的宿主模型,需要同时依赖于VMM和宿主机操作系统的安全。
Hypervisor模型在拥有虚拟化高效率的同时也暴露出其缺点。由于VMM完全拥有物理资源,因此,VMM需要进行物理资源的管理,包括设备的驱动,设备驱动开发的工作量巨大,这对Hypervisor模型来说是个很大的挑战。事实上,在实际的产品中,基于Hypervisor模型的VMM通常会根据产品定位,有选择地挑选一些I/O设备来支持,而不是支持所有的I/O设备。此外,在基于Hypervisor模型中,很多功能必须在VMM中重新实现,例如调度和电源管理,无法向宿主模型那样借助宿主机操作系统。
宿主模型
与Hypervisor模型不同,在宿主模型,物理资源由宿主机操作系统管理。宿主机操作系统是传统操作系统,如Windows、Linux等,这些传统操作系统并不是为虚拟化而设计的,因此本身并不具备虚拟化功能,实际的虚拟化功能由VMM来提供。VMM通常是是宿主机操作系统独立的内核模块,有些实现中还包括用户态进程,如负责I/O虚拟化的用户态设备模型。VMM通过调用宿主机操作系统的服务来获得资源,实现处理器、内存和I/O设备的虚拟化。VMM创建出虚拟机后,通常间虚拟机作为宿主机操作系统的一个进程参与调度。
下图展示了宿主模型的架构。由于宿主机操作系统控制所有的物理资源,包括I/O设备,因此,设备驱动位于宿主机操作系统中。VMM(图中的虚拟机管理内核模块)则包含了处理器虚拟化模块和内存虚拟化模块。图中的设备模型实际上也是VMM的一部分,在具体实现中,可以将设备模型放在用户态,也可以放在内核态中。
宿主模型的优缺点和Hypervisor模型恰好相反。宿主模型最大的优点是可以利用现有操作系统的设备驱动程序,VMM无须为各类I/O设备重新实现驱动程序,可以专注于物理资源的虚拟化。考虑到I/O种类繁多,I/O设备驱动程序开发的工作量非常大,因此,这个优点意义重大。此外,宿主模型也可以利用宿主操作系统的其他功能,例如调度和电源管理等,这些不需要VMM重新实现就可以直接使用。
宿主模型当然也有缺点。由于物理资源由宿主机操作系统控制,VMM需要调用宿主机操作系统的服务来获取资源进行虚拟化,而那些系统服务在设计之初没有考虑虚拟化的支持,因此,VMM虚拟化的效率和功能会受到一定影响。此外,在安全方面,由于VMM是宿主机操作系统内核的一部分,因此,如果宿主机操作系统内核是不安全的,相对较容易被攻破。换言之,虚拟机的安全不仅依赖于VMM的安全,也依赖于宿主操作系统的安全。与现有的操作系统架构相比,宿主模型在架构上并没有提高安全性。
混合模型
混合模型是Hypervisor模型和宿主模型的结合体。VMM依然位于最底层,拥有所有的物理资源。与Hypervisor模型不同的是,VMM会主动让出大部分I/O设备的控制权,将它们交由一个运行在特权虚拟机中的特权操作系统来控制。相应的,VMM虚拟化的职责也被分担。处理器和内存的虚拟化依然由VMM来完成,而I/O的虚拟化则由VMM和特权操作系统共同合作完成。
下图展示了混合模型的架构。I/O设备由特权操作系统控制,因此,设备驱动模块位于特权操作系统中。其他物理资源的管理和虚拟化由VMM完成,因此,处理器管理代码和内存管理代码处在VMM中。
I/O设备虚拟化由VMM和特权操作系统共同完成,因此,设备模型模块位于特权操作系统中,并且通过相应的通信机制与VMM合作。
混合模型集中了上述两种模型的优点。VMM可以利用现有操作系统的I/O设备驱动程序,不需要另外开发。VMM直接控制处理器、内存等物理资源,虚拟化的效率也比较高。在安全方面,如果对特权操作系统的权限控制得当,虚拟机的安全性只依赖于VMM。
当然,混合模型也有缺点。由于特权操作系统运行在虚拟机上,当需要特权系统提供服务时,VMM需要切换到特权操作系统,这里面就产生了上下文切换的开销。当切换比较频繁时,上下文切换的开销会造成性能的明显下降。处于性能方面考虑,很多功能还是必须在VMM中实现,无法借助特权操作系统,如调度程序和电源管理等。
典型虚拟化产品及特点
VMware
VMware是x86虚拟化软件的主流厂商之一。VMware的5位创始人中3位曾经在斯坦福大学研究操作系统虚拟化,项目包括SimOS系统模拟器和Disco虚拟机监控器。1998年,他们与另外两位创始人共同创建了VMware公司,总部位于美国加州Palo Alto。VMware在2003年被EMC收购,称为EMC的全资子公司,2015年10月,EMC被DELL收购。VMware于2007年在纽约证交所上市。
VMware提供一系列的虚拟化产品,产品的应用领域从服务器到桌面,产品可以运行在Windows、Linux和Mac OS上。此外,基于Hypervisor架构的VMware ESX Server则直接运行在物理硬件之上,无须操作系统。下面是VMware主要产品的简介,包括ESX Server、VMware Server、VMware Workstation和VMware Fusion。
VMware ESX Server 3是VMware的旗舰产品。ESX Server基于Hypervisor模型,在性能和安全性方面都得到了优化,是一款面向企业级应用的产品。VMware ESX Server支持完全虚拟化,可以运行Windows和Linux等客户机操作系统。VMware ESX Server也支持类虚拟化,可以运行Linux 2.6.21以上的客户机操作系统。ESX Server早期版本采用软件虚拟化的方式,基于Binary Translation技术。在新版本中已经采用硬件虚拟化的技术,支持Intel VT技术和AMD-V技术。
VMware Server之前叫VMware GSX Server,是VMware面向服务器端的入门机产品。VMware Server采用宿主模型,宿主机操作系统可以是Windows或者Linux。VMware Server的功能和ESX Server类似,但是在性能和安全性上与ESX Server有所差距。VMware Server也有自己的优点,由于采用宿主模型,因此VMware Server支持的硬件种类要比ESX Server多。此外VMware Server是免费的。
VMware Workstation是VMware面向桌面的主打产品。与VMware Server类似,VMware Workstation也是基于宿主模型,宿主机操作系统可以是Windows或者Linux。VMware Workstation也支持完全虚拟化,可以运行Windows和Linux等客户机操作系统。与VMware Server不同,VMware Workstation专门针对桌面应用做了优化,如为虚拟机分配USB设备,为虚拟机显卡进行3D加速等。
VMware Fusion也是VMware面向桌面的一款产品,功能和VMware Workstation基本相同,主要区别在于VMware Fusion的宿主机操作系统是基于Intel Mac硬件平台的Mac OS,而VMware Workstation则运行在Windows和Linux上。
由于VMware起步比较早,因此VMware产品具有以下几个特点:
- 功能丰富。几乎大部分的虚拟化功能都由相应的VMware产品对应。
- 配置和使用方便。VMware开发了非常易于使用的配置工具和用户界面。
- 稳定,适合企业使用。
Microsoft
微软在虚拟化产品方面起步比VMware晚,但是在认识到虚拟化的重要性之后,微软通过外部收购和内部开发,推出了一系列虚拟化产品,目前已经形成比较完整的虚拟化产品线。微软的虚拟化产品涵盖了服务器虚拟化(Virtual Server,Windows Server 2008)、桌面虚拟化(Virtual PC)、应用虚拟化(SoftGrid)、表现虚拟化(Terminal Service)、存储虚拟化(Windows Storage Server)和网络虚拟化等领域。此外,微软还开发了集中式的管理工具System Center用于虚拟化的管理。
Virtual PC是面向桌面的虚拟化产品,最早由Connectix公司开发,后来改产品被微软公司收购。Virtual PC是基于宿主模型的虚拟机产品,宿主机操作系统是Windows。早期版本也采用软件虚拟化方式,基于Binary Translation技术。新版本已经支持硬件虚拟化技术,支持Intel VT技术和AMD-V技术。2006年,微软将该软件免费。
Virtual Server是面向服务器的入门级虚拟化产品。与Virtual PC一样,Virtual Server基于宿主模型,宿主操作小婷可以是Windows XP和Windows Server 2003等。Virtual Server从2005 R2 SP1版本开始支持硬件虚拟化技术,包括Intel VT技术和AMD-V技术。2006年,微软将该产品免费。
Windows Server是微软推出的新一代服务器操作系统,其中一项重要的新功能是虚拟化功能。Windows Server的虚拟化架构采用的是混合模型,其重要组件之一Hyper-V作为Hypervisor运行在最底层,Windows Server本身作为特权操作系统运行在Hyper-V之上。Windows Server采用硬件虚拟化技术,必须运行在支持Intel VT技术或者AMD-V技术的处理器上。Windows Server的虚拟化功能是纯64位,只运行在Windows Server 64位版本中。Windows Server在性能和功能上都优于Virtual Server,功能上包括支持32位/64位客户机操作系统,支持对称多处理器的客户机操作系统等。
Microsoft虚拟化产品的特点在于和Windows操作系统结合得非常好,在Windows下非常易于配置和使用。
Xen
Xen是一款基于GPL授权方式的开源虚拟机软件。Xen起源于英国剑桥大学Ian Pratt领导的一个研究项目,之后,Xen独立出来成为一个社区驱动的开源软件项目。Xen社区吸引了许多公司和科研院所的开发者加入,发展非常迅速。之后,Ian成立了XenSource公司进行了Xen的商业化应用,并且推出了基于Xen的产品Xen Server。2007年,Citrix公司收购了XenSource公司,继续推广Xen的商业化应用。Xen开源项目本身被独立到www.xenproject.org,并且成立了Xen AB(Xen Project Advisor Board,Xen项目指导委员会)来管理Xen开源项目,主要工作包括管理Xen项目的路线图、Xen商标的授权政策等。
从技术角度来说,Xen基于混合模型,如下图所示。特权操作系统(Domain 0)可以是Linux、Solaris以及NetBSD,理论上,其他操作系统也可以移植作为Xen的特权操作系统。Xen最初的虚拟化思路是类虚拟化,通过修改Linux的内核,实现处理器和内存的虚拟化,通过引入I/O的前度驱动/后端驱动(Front/Backend)架构实现设备的类虚拟化。Xen类虚拟化虚拟机的性能接近物理机。
随着Xen社区的发展壮大,硬件完全虚拟化技术也加入到Xen中,Xen 3.0支持基于Intel VT和AMD-V硬件技术的完全虚拟化。图中VMX Domain是支持完全虚拟化的虚拟机。Hypervisor通过硬件提供的功能实现处理器、内存和I/O的虚拟化,其中,I/O虚拟化中的设备模型借用了另外一个开源项目QEMU,利用QEMU的设备模拟代码来完成I/O设备的虚拟化。此外,类虚拟化中的前端驱动/后端驱动的架构也可以应用在VMX Domain中,用于提高I/O设备的性能。
Xen支持多种硬件平台,官方的版本支持包括x86_32、x86_64、IA64和PowerPC架构。由于Xen是开放源代码的,Xen也被开发者移植到其他架构上。
Xen目前已经比较成熟,基于Xen的虚拟化产品也很多,如Ctrix、VirtualIron、Redha和Novell等都有相应的产品。Xen目前还在发展当中,一些新的技术和功能还在不断地被加入。
作为开源软件,Xen的特点如下。
- 可移植性非常强,开发者可以将其移植到其他平台,也可以将其修改用于项目研究等。
- 独特的类虚拟化支持,提供了接近于物理机的性能。
Xen的易用性还有待加强。
KVM
KVM(Kernel-based Virtual Machine)也是一款基于GPL授权方式的开源虚拟机软件。KVM最早由Qumranet公司开发,在2006年10月出现在Linux内核的邮件列表上,并于2007年2月被集成到了Linux 2.6.20内核中,成为内核的一部分。
KVM的架构如下图所示。KVM采用的是基于Intel VT技术的硬件虚拟化方法,并也是结合QEMU来提供设备虚拟化。此外,Linux社区中已经发布了KVM的类虚拟化扩展。KVM是Hypervisor模型。
KVM支持多硬件平台,包括IA32、IA64、S390和PowerPC。KVM也可以移植到其他操作系统上。
KVM的特点在于和Linux内核结合得非常好,因此KVM继承了Linux的大部分功能。当然,和Xen一样,作为开源软件,KVM的移植性也很好。