共计 6914 个字符,预计需要花费 18 分钟才能阅读完成。
title: 云计算_06_虚拟化技术_CPU硬件辅助虚拟化 date: 2022-04-29 18:11:05.0 updated: 2022-05-10 13:09:06.9 url: /archives/cloud061 categories:
- 云计算 tags:
- 云计算
- 虚拟化
1 CPU硬件辅助虚拟化
之前我们已经介绍了CPU的三种虚拟化方式,也了解了KVM是基于硬件辅助的虚拟化,有些东西还是没说明白,比如VMCS是怎么用的,为什么要区分Root和Non-Root模式?引入根模式和非根模式之后,出现了八个级别,那么这几个级别又是运行的什么东西,跑的什么程序?虚拟机和宿主机之间又是怎么切换的?等等问题。由于本人才疏学浅,本文可能并不能全部解决这些问题,但是可以有一定的了解。
注:本文大多来自网上的公开资料,参考文献在文末,只是加了本人的一点看法在其中。
再来谈一谈是如何处理敏感指令的特权指令的问题的。 个人理解,由于原有X86系统Ring0-Ring3共4个级别,Linux只是用Ring0 和Ring 3,在没有虚拟化的场景下,操作系统OS可直接执行特权指令,运行在Ring 0级,而加入虚拟化环境后,在没有CPU硬件辅助虚拟化的技术下,就拿Linux来作为Guest OS来说,此时VMM层控制所有硬件资源,VM中的Guest OS只能处于Ring 1-Ring3级别,此时Guest OS不能直接对底层硬件进行操作,所以全虚拟化,也通过了VMM层来解决这个问题,半虚拟化通过Hypercall解决这个问题。但是前两种方式,存在软件实现,性能、兼容性等问题。
于是硬件辅助虚拟化出现,而硬件辅助虚拟化又需要怎么实现CPU虚拟化,什么是CPU虚拟化呢,目的是什么?我们先聊一下虚拟化的历史。
1961年 — IBM709机实现了分时系统,将CPU占用切分为多个极短(1/100sec)时间片,每一个时间片都执行着不同的任务。通过对这些时间片的轮询,这样就可以将一个CPU虚拟化或者伪装成为多个CPU,并且让每一颗虚拟CPU看起来都是在同时运行,这就是虚拟机的雏形。后来的system360机都支持分时系统。 1972年 — IBM正式将system370机的分时系统命名为虚拟机。 1990年 — IBM推出的system390机支持逻辑分区,即将一个cpu分为若干份(最多10份),而且每份cpu都是独立的,也就是一个物理cpu可以逻辑的分为10个cpu。
直到IBM将分时系统开源后,个人PC终于临来了虚拟化的开端,后来才有了上述的虚拟机软件的发展。所以至今为止仍然有一部分虚拟机软件应用分时系统 作为虚拟化的基础实现。就拿CPU全虚拟化来说,通过模拟这种方式实现的全虚拟化,那么VM是不清楚自己运行在虚拟机中的,因为它的OS发出的指令操作,最终都会落到底层的CPU上执行,就相当于分时使用CPU。
总结一下,虚拟化的最终目的 :使用逻辑来表示资源,从而摆脱物理限制的约束。提高物理资源的利用率。
而在虚拟化的过程中,发现Guest OS的特权指令以及敏感指令的问题,从解决这个问题的角度出发,就划分了CPU的虚拟化种类,什么全虚拟化、半虚拟化、硬件辅助虚拟化,目的是一样的,用逻辑表示资源,造成1个物理资源数量更多或者容量更大的假象!那么如何从硬件上支持这种虚拟化呢?
CPU虚拟化的问题,就转变为了如何解决敏感指令的问题,硬件辅助虚拟化方式就是引入了Root和Non-Root模式,将原来对CPU来说的4个级别,划分成了8个级别,CPU知道自己处于Root模式还是Non-Root模式,VMM运行在Root模式中,而Guest VM则运行在Non-Root的受限模式中,当VM中的OS执行到敏感指令时,此时CPU处于Non-Root模式,就会产生VM-Exit事件,通过VMCS转入Root模式,VMM接管CPU,模拟结果后,发出VM-Entry,重新加载vcPU状态,返回VM。
2 硬件辅助虚拟化 AMD/Intel
目前主要有Intel的VT-x和AMD的AMD-V这两种技术。其核心思想都是通过引入新的指令和运行模式,使VMM和Guest OS分别运行在不同模式(ROOT模式和非ROOT模式)下,且Guest OS运行在Ring 0下。通常情况下,Guest OS的核心指令可以直接下达到计算机系统硬件执行,而不需要经过VMM。当Guest OS执行到特殊指令的时候,系统会切换到VMM,让VMM来处理特殊指令。
我们就主要讲Intel的VT-x技术,AMD-V同理。
3 Intel VT-x技术
经过前面的介绍,我们也知道了Intel VT技术包含CPU、内存和I/O三方面的虚拟化技术。CPU硬件辅助虚拟化技术,分为对应安腾架构的VT-i(Intel Virtualization Technology for ltanium)和对应x86架构的VT-x(Intel Virtualization Technologyfor x86)两个版本。内存硬件辅助虚拟化技术包括EPT(Extended Page Table)技术。I/O 硬件辅助虚拟化技术的代表Intel VT-d(Intel Virtualization Technology for Directed I/O)。
Intel VT-x解决了X86架构敏感指令和特权指令的问题,引入了Root和Non-Root模式,也就是根模式和非根模式,可使未经修改的Guest OS运行在特权级0,同时减少VMM对Guest OS的干预。CPU硬件辅助虚拟化技术简要说明流程图:
效法IBM 大型机,VT-x提供了2 个运行环境:根(Root)环境和非根(Non-root)环境。根环境专门为VMM准备,很像原来没有VT-x 的x86,只是多了对VT-x 支持的几条指令。非根环境作为一个受限环境用来运行多个虚拟机。
如上图所示,根操作模式与非根操作模式都有相应的特权级0至特权级3。VMM运行在根模式的特权级0,GuestOS的内核运行在非根模式的特权级0,GuestOS的应用程序运行在非根模式的特权级3。运行环境之间相互转化,从根环境到非根环境叫VMEntry;从非根环境到根环境叫VMExit。VT-x定义了VMEntry操作,使CPU由根模式切换到非根模式,运行客户机操作系统指令。若在非根模式执行了敏感指令或发生了中断等,会执行VMExit操作,切换回根模式运行VMM。
VMX操作
VMX(Virtual Machine Extension)是Intel 64和IA-32架构处理器级别的功能,用于支持虚拟化。VMX支持两种类型的软件:
- Virtual-machine monitor(VMM),VMM对处理器和平台硬件具有完全的控制权限。VMM为客户软件提供虚拟处理器的抽象,从而让客户软件能够直接在逻辑CPU上跑。
- 客户软件(Guest software)。每个虚拟机(VM)就是一个客户软件运行环境。
根操作模式与非根操作模式之间的转换称作VMX转换,从根操作模式转换到非根操作模式称作VMX进入(VMX Entry),相反从非根操作模式转换到根操作模式称作VMX退出(VMX Exit)。
当处理器运行于VMX根操作模式的时候,它类似于运行在没有打开VMX功能的环境下,跟不打开VMX功能的主要区别是,在VMX根操作模式中,可以执行VMX指令,但是当加载数据到某些特定的寄存器的时候,也会受到一些限制。
当处理器运行于VMX非根操作模式的时候,处理器的某些行为被限制住或者被更改以便实现虚拟化,即某些指令或者事件会引起VM exits,从而让处理器从VMX非操作模式退出到根操作模式的VMM里,从而让VMM去模拟这些指令的行为从而实现虚拟化。
根模式与非根模式之问的相互转换是通过VMX操作实现的。VMM 可以通过VMXON 和VMXOFF打开或关闭VT-x。如下图所示:
VMX操作模式流程:
- VMM执行VMXON指令进入VMX操作模式。
- VMM可执行VMLAUNCH指令或VMRESUME指令产生VM Entry操作,进入到Guest OS,此时CPU处于非根模式。
- Guest OS执行特权指令等情况导致VM Exit的发生,此时将陷入VMM,CPU切换为根模式。VMM根据VMExit的原因作出相应处理,处理完成后将转到2),继续运行GuestOS。
- VMM可决定是否退出VMX操作模式,通过执行VMXOFF指令来完成。
VMCS 结构
在虚拟化中,为了实现vCPU,既要模拟CPU的运行,又要记录vCPU的状态(包括对vCPU运行的控制信息),在Intel x86处理器的VMX(Virtual Machine Extension)功能中,通过引入根运行模式(VMX root operation)和非根模式(VMX non-root operation),直接让vCPU运行在逻辑CPU上,在软件上省去了对vCPU运行的模拟,同时也大大提升了性能。剩下的就是对vCPU状态的记录了,为此Intel引入了VMCS(Virtual Machine Control Structure)功能。
VMCS是保存在内存中的数据结构,其包括虚拟CPU的相关寄存器的内容及相关的控制信息。CPU在发生VM Entry或VM Exit时,都会查询和更新VMCS。VMM也可通过指令来配置VMCS,达到对虚拟处理器的管理。VMCS架构图如下图所示:
VMCS中保存了客户机和宿主机之间切换的vCPU/CPU的状态和控制信息,有点类似Linux中的进程上下文信息。 VMCS(Virtual Machine Control Structure)是Intel x86处理器中实现CPU虚拟化,记录vCPU状态的一个关键数据结构。该数据结构主要包含如下六方面的信息:
- Guest-state area,即vCPU的状态信息,包括vCPU的基本运行环境(如通用寄存器等)和一些非寄存器信息,如当前vCPU是否接收中断,是否有挂起的exception,VMCS的状态等等。
- Host-state area,即主机物理CPU的状态信息,因为物理CPU是在主机CPU和vCPU之间来回切换运行的,所以在VMCS中既要记录vCPU的状态,也需要记录主机CPU的状态,这样vCPU才能有足够的信息恢复到原来主机CPU的状态,继续主机CPU的运行。其包含的具体信息和前面记录的vCPU的状态信息大体相同。
- VM-execution control fields,该方面信息主要用于对vCPU的运行行为进行控制,这是VMM对vCPU进行配置最复杂的一部分,如
- 控制vCPU在接收到某些中断事件的时候,是否直接在vCPU中处理掉,即虚拟机直接处理掉该中断事件还是需要退出到VMM中,让VMM去处理该中断事件。
- 是否使用EPT(Extended Page Table)功能。
- 是否允许vCPU直接访问某些I/O口,MSR寄存器等资源。
- vCPU执行某些指令的时候,是否会出发VM Exit等等。
- VM-exit control fields,即对VM Exit的行为进行控制,如VM Exit的时候对vCPU来说需要保存哪些MSR寄存器,对于主机CPU来说需要恢复哪些MSR寄存器。
- VM-entry control fields,即对VM Entry的行为进行控制,如需要保存和恢复哪些MSR寄存器,是否需要向vCPU注入中断和异常等事件(VM Exit的时候不需要向主机CPU注入中断/异常事件,因为可以让那些事件直接触发VM Exit)。
- VM-exit information fields,即记录下发生VM Exit发生的原因及一些必要的信息,方便于VMM对VM Exit事件进行处理,如vCPU访问了特权资源造成VM Exit,则在该区域中,会记录下这个特权资源的类型,如I/O地址,内存地址或者是MSR寄存器,并且也会记录下该特权资源的地址,好让VMM对该特权资源进行模拟。
VMCS 操作
每个vCPU都有自己的一份VMCS数据结构,并且在同一个VM中的vCPU可能会通过VMCS中的指针共享一些数据,如I/O bitmap,MSR bitmap和EPT表等。VMCS的大小需要查看VMX capability MSR IA32_VMX_BASIC寄存器来决定,不同的处理器可能要求的不一样,最大不超过4KB。
每个VMCS都会有一个8字节的头部,前面4字节主要用来表示VMCS的版本号并且表示VMCS是否为shadow VMCS。然后从偏移量8开始,就是VMCS的数据区,不同版本号的VMCS数据区分布会不一样。
当VMCS数据结构被load到逻辑CPU上(即执行VMPTRLD指令)后,处理器并没法通过普通的内存访问指令去访问VMCS,如果那样做的话,会引起处理器报错,唯一可用的方法就是通过VMREAD和VMWRITE指令去访问。
VMCS数据结构也会记录下当前vCPU的状态,基本上VMCS的状态由三个域来表示:
- Active或Inactive,表示该VMCS是否处于激活状态,即对于逻辑CPU来说,该VMCS是否是直接可用的,有点类似于Linux进程的可运行状态(虽然可运行,但并不意味着现在就必须运行)
- Current或Non Current,该状态表示当前逻辑CPU执行VMX指令(如VMCLEAR,VMLAUNCH,VMRESUME等)时的目标VMCS(或者可以理解为目标vCPU),这些VMX指令的目标都是状态为Current的VMCS所代表的vCPU。
- Clear或者Launched,用于表示VMCS所对应的vCPU是否有在逻辑CPU上执行过,如果没有,则为Clear状态,如果有,则为Launched状态,Launched状态的VMCS也可以通过VMCLEAR指令恢复到Clear状态。当一个vCPU从一个逻辑CPU迁移到另外一个逻辑CPU的时候,需要先将相应VMCS的状态恢复到Clear,撇清该VMCS根原本逻辑CPU的关系,然后在另外一个逻辑CPU上执行VMPTRLD,将该VMCS和新的逻辑CPU建立联系。
对于VMM而言,配置并运行一个VM的最基本步骤如下所示:
- 根据IA32_VMX_BASIC MSR寄存器提供的VMCS region大小信息,在内存中创建一个4KB对齐的VMCS region(为了防止被直接内存访问到,可以将该page frame从页表中删除);
- 根据IA32_VMX_BASIC MSR寄存器提供的VMCS版本信息,初始化新创建的VMCS region中的版本信息,并且将VMCS前4字节的bit31设置为0(表示该VMCS不是shadow VMCS);
- 针对新创建的VMCS region执行VMCLEAR指令,这会对新创建的VMCS region进行初始化,并且将VMCS的状态设置为Clear;
- 针对新创建的VMCS region执行VMPTRLD指令,该指令会将新创建的VMCS region设置为逻辑CPU的当前VMCS,即新创建的VMCS从原来的Not Current状态变为Current状态,后面所有的VMX相关指令操作的对象都将是新创建的VMCS;
- 通过一系列的VMWRITE指令对VMCS中的Host-state area进行初始化,该区域将记录VM返回到VMM时,或者是VM Entry不成功时,逻辑CPU的状态及执行位置等信息;
- 通过VMWRITE指令对VMCS中VM-Exit control field,VM-Entry control field和VM execution control field等区域进行设置;
- 通过WMWRITE指令对VMCS中guest state area区域进行设置,该区域表示了VM中vCPU的执行时vCPU的状态,起始执行位置等信息;
- 执行VMLAUNCH指令,开始vCPU的执行。通过VMM需要判断VMLAUNCH的返回结果,确定vCPU是否真正被执行,还是因为某些逻辑冲突导致vCPU没有被执行就返回。
注意:在设置VMCS的各个数据区域的时候,是有顺序讲究的,需要先设置Host-state area,这样可以防止后边步骤出现问题,CPU无法返回到正确的状态和位置,另外页可以减少后面设置对Host CPU状态的干扰。
下面这张图就展示了不同的VCPU的切换和状态转换过程:
可以看到,不同的VMCS的切换就像Linux中不同的进程在逻辑CPU上切换,通过时分复用的方式共享着物理CPU的资源。