虚拟化技术 - CPU 虚拟化
本文分享自天翼云开发者社区《虚拟化技术- CPU虚拟化》,作者:谢****悦
物理机器是由 CPU,内存和 I/O 设备等一组资源构成的实体。虚拟机也一样,由虚拟 CPU,虚拟内存和虚拟 I/O 设备等组成。VMM(VM Monitor)按照与传统 OS 并发执行用户进程的相似方式,仲裁对所有共享资源的访问。本文将分别讨论 CPU 虚拟化、内存虚拟化和 I/O 虚拟化技术的原理和实现。
在虚拟化的平台上,虚拟机(guest VM)所使用的多个虚拟 CPU(以下称 vCPU)可能是共享同一个物理 CPU(以下称 pCPU)的。VMM 负责 vCPU 的调度,当一个 vCPU 被调度到获得 pCPU 的使用权后,基于该 vCPU 运行的 guest OS 又可以调度 OS 中的各个线程/进程了。也就是说,guest OS 中的各个线程/进程分时复用了 vCPU,而各个 vCPU 又分时复用了 pCPU。
为了从硬件上提供对 vCPU 调度和切换的支持,Intel 推出了被称为 VT-x(Virtualization Technology for x86)的 CPU 虚拟化扩展技术,用户可通过 VMXON/VMXOFF 指令打开/关闭这个功能。和 Intel 亦敌亦友的 AMD 也推出了被称为 AMD-V 的对应技术。
在 Linux 中,从用户空间 trap 到内核空间可以通过 system call 或者 interrupt/exception。以 system call 基于 x86 的实现为例,早期 x86 提供的 trap 方法是 int 0x80 这样的 software interrupt 机制,而后改成了 SYSENTER/SYSEXIT 的指令对,现在则已经被速度更快的 SYSCALL/SYSRET 取代了。
类似地,在 VT-x 中,从 guest VM 进入 VMM(这个过程被称为 VM exit)通常有三种方式:
· 执行 VMCALL 指令,这种方式被称为 hyper call,跟执行 SYSCALL 指令实现的 system call 原理差不多。
· 发生了硬件中断或软件异常。
· guest VM 执行了一些敏感指令。有一些敏感指令并不会产生 VM exit,比如 SYSENTER;有一些敏感指令则可以根据下面将要介绍的 VM executation control fields 配置来选择是否产生 VM exit。
进入 VMM 就意味着从 non-root mode 进入了 root mode,反之,从 VMM 返回 guest VM(这个过程被称为 VM entry)则是重新回到了 non-root mode,mode 的切换意味着上下文(context)的保存和恢复。
上下文其实是个难以定义的概念,它是从 CPU 的角度引出的,简单地说,上下文就是程序(进程/中断)运行时所需要的寄存器的最小集合,这些寄存器的后面可能代表着程序运行的一类资源。
上下文切换是指程序从一种状态切换到另一种状态(比如从用户态切换到内核态),或者从一个程序切换到另一个程序(比如进程切换)时,导致上下文相关寄存器的值变化的行为。对于上下文切换时不需要改变的寄存器,也可以说它不是该程序的上下文。
VMCS
在 Linux 中,一个进程的相关信息保存在 task_struct 中。虚拟机的上下文比进程的上下文更为复杂,在 VT-x 中,由 VMCS(Virtual-Machine Control data Structures)负责保存 vCPU 需要的相关状态和上下文信息。
VMCS 在使用时需要和 pCPU 绑定。一个 pCPU 可以对应多个 vCPU,而一个 vCPU 对应一个 VMCS,但在任意给定时刻,一个 pCPU 上只能运行一个 vCPU(就像在多线程调度中,某一时刻,一个 CPU 上只能运行一个线程一样)。
因此,一个 pCPU 只能绑定一个 VMCS,一个 VMCS 也只能与一个 pCPU 绑定,可分别通过 VMPTRLD/VMCLEAR 指令建立/解除两者的绑定关系。VMCS 存放在内存中,一个 VMCS 占据 4KB 大小,由 6 个区域组成:
· Guest state area,用于保存 CPU 在 non-root mode 下运行时的状态。当发生 VM exit 的时候,CPU 将自己当前的状态保存到 guest state area 中,当发生 VM entry 的时候,guest state area 保存的状态将被自动加载到 CPU 中。
其实也不用一口气将所有寄存器的值都恢复,反正都是保存在 VMCS 中的,可以等到该寄存器真正被 guest 使用到时再恢复,这就是 Lazy Save/Restore,其基本思想是尽量将寄存器的保存/恢复延迟到最后一刻,减少无用功,提高上下文切换的效率。这种思想在 Linux 的实现中也比比皆是,比如 copy on write, demand paging 等,拖延症也不见得是件坏事哈。
· Host state area,用于保存 CPU 在 root mode 下运行时的状态。需要保存的寄存器和 guest state area 是差不多的,但是保存/恢复的过程是刚好反过来的。
· VM executation control fields,用于控制 non-root 模式下 CPU 的行为。出于优化的目的,VMM 可以让某些敏感指令不产生 VM exit,以减少 mode 切换带来的上下文开销,而这就是由 VM execution control 来实现的。
比如读取 timestamp 的 RDTSC 指令,在一些延时函数的实现中,该指令会被频繁使用,如果每次 guest 执行该指令的时候都 trap 到 VMM,那系统开销就太大了,这时 VMM 可以选择每隔一段时间读取物理 CPU 真实的 timestamp 值,然后填写 guest 的 timestamp 虚拟寄存器,来达到模拟 RDTSC 指令的效果。
· VM exit control fields,用于规定 VM exit 时 CPU 的行为,比如是否应答外部中断。
· VM exit information fields。VMM 除了通过 VM exit control fields 来控制 VM exit 的行为,还需要知道 VM exit 的相关信息(比如 trap 的具体原因),这些信息就是保存在 VM exit information fields 中的。
· VM entry control fields,用于控制 VM entry 的过程,比如后续的文章要介绍的中断注入。
VM executation control 可理解为 what to trap,就是哪些 event 会引起 trap,哪些不会。VM exit control 可理解为 how to trap,VM exit information 则可理解为 why to trap。读写 VMCS 这段内存空间需要使用专门的 VMREAD 和 VMWRITE 指令。
小结一下,一个完整的 VT-x 使用流程是这样的:首先需要通过 CPUID 指令检查当前 CPU 是否支持虚拟化扩展。如果支持,则通过 VMXON 使能 VT-x,建立 VMCS 并通过 VMPTRLD 绑定物理 CPU。
评论