这种将物理硬件分配给多个使用者的技术,叫做“时分复用”。计算机操作系统的任务调度模块,实质上提供的就是将CPU以“时分复用”的方式给不同任务使用的机制。
那么,如果在虚拟化系统中,也利用时分复用机制,将一个物理CPU或HT分配给多个虚拟机使用,就可以让多个虚拟机共用1个物理HT,也就是在虚拟机操作系统视角看到的CPU数量多于宿主机的物理HT数量。这就是所谓的“超分配”。
在Linux自带的KVM中,超分配实际上是默认的方式。
下图是Linux KVM中,vCPU的实现方式:
一般在每台物理服务器上,会有2-4个物理CPU,每个物理CPU内部都会有若干个Core。Intel的处理器在开启了VT技术后,每个Core有两个HT(Hyper-Threads),在宿主机上的Linux操作系统看来就是两个CPU。
如某台服务器配置了2颗Intel 5218R CPU,每颗有20核,整机40核,那么,服务器上的操作系统可以检测到80个HT,也就是在操作系统看来,整机有80个CPU。
在Linux侧,KVM中,每一个VM就是一个用户空间的QEMU进程,而分配给VM的vCPU就是该QEMU进程的一个线程Thread。如某VM有4个vCPU,那么,这个QEMU进程中就有4个Thread。
由于Linux是一个SMP操作系统,在没有特殊设置的前提下,Linux的每个线程或进程,都可以被Linux调度到任意一个CPU。如果用户期望虚拟机的vCPU独占一个HT,可以通过把vCPU对应的QEMU进程下的thread绑定到HT来实现。
如图,VM1的vCPU0和vCPU1被绑定到两个硬件HT,每个vCPU都有对应的硬件HT。
而VM2,采取了“时间管理大师”的方式进行调度,如图,VM2的vCPU0和vCPU1,对应的QEMU线程在同一个硬件HT上运行,CPU会利用时分复用的方式调度两个QEMU的线程,也就是将一个硬件HT作为2个vCPU提供给虚拟机使用。
看到这里,大家可能会有一个疑问:VM上的vCPU是运行了包括GuestOS本身在内的多任务的,而它又只是QEMU的一个线程,这不是矛盾的吗?这个线程实际上到底会让物理CPU的HT执行什么样的指令呢?
Intel通过增加了VT-X系列机制,在VM Entry和VM Exit中让一个HT可以在宿主机状态和虚拟机状态之间切换。
实际上,当VM的vCPU发生VM Exit,退回到hypervisor的时候,就是退回到QEMU中,也就是当前VM GuestOS上的程序代码在运行时被切掉,CPU转而执行QEMU的代码!
如果这个vCPU是超分配出来的,也就是对应的QEMU线程会与其他VM的vCPU共用一个HT,那么,在系统的时钟中断中,会无条件切断虚拟机vCPU在这个HT上的运行,并引发VM Exit,中断处理程序会做下面这些事情:
- 保存vCPU的运行现场(各寄存器),特别是保存在堆栈中的切断时的程序指针(CS:IP)、标志位(EFLAGS)、堆栈选择子SS和堆栈指针SP;
- 进入Linux的任务调度程序;
- 确定该HT上应当运行的下一个任务,并将该任务上次被时钟中断切掉时的现场恢复到CPU的各寄存器;
- 最后执行iret指令,把下一个任务的SS,SP,EFLAGS和CS:IP数据恢复到CPU的这两个寄存器,CPU开始执行下一个任务;
由于这个HT已经被虚拟化KVM所管理,上面只会运行分配给不同VM的vCPU对应的线程,因此,下一个任务一定是其他VM对应的QEMU里面,拉起vCPU的线程。这个线程会再次通过VM Entry,转到上次这个vCPU被时钟中断切断的执行现场,继续执行该vCPU服务于虚拟机时的程序。
这样,Linux操作系统的“时间管理大师”功能就可以用于虚拟机对CPU资源的时分复用啦!