VMM 虚拟机器监控

引言

很多年前,IBM 在售卖它们及其昂贵的大型机给大小组织机构。一个共同的需求在客户中提出,如果客户想要同时在这些硬件上运行不同的操作系统怎么办?还有,有些应用程序部署在某种 OS 上,另外些应用程序需要部署在另外的 OS 上。

IBM 为此种需求提供了一个解决方案,那就是 Virtual Machine Monitor ( VMM ),也被叫做 hypervisor (这个名字可能更为人所知).

VMM 工作在 OS 和 硬件之间,给予操作系统一种 I am in the control of the while machine 的假象,但实际上是多重操作系统共享底层硬件。

VMM 扮演了一种 运行操作系统的操作系统 的角色,比 Guest OS 更底层一些,让 Guest OS 认为他们就工作在 Bare Machine 上面,因为提供对于 Guest OS 的透明度是 VMM 的首要任务。

所以问题来了,怎样对 Guest OS 提供这样一层虚拟化呢?

动机:为什么是 VMM ?

时至今日(作者指的是到什么时间为止?因为我还没研究过其他家的虚拟化技术跟 VMM 的关系),VMMs 因为诸多的原因重新流行起来。

人们才不同的操作系统(或者是不同的操作系统版本)上运行着不同的服务,每个机器都只被轻量地使用而已。这种情况下,虚拟化技术允许 IT 管理员在较少的硬件版本基础上管理不同的操作性。

同时,虚拟化记住也在桌面电脑上流行起来,需要用户在一台电脑上会同时允许着 Linux 和 MacOS,但同时也运行着 Native 的应用程序。

另外一个原因是对开发者来说,方便测试和调试。

这波趋势从 1990 年中后期开始流行起来,由斯坦福大学的教授 Mendel Rosenblum 发起。他的团队在多处理器上开发一个在 Disco 系统,是一个虚拟机监控软件,这最终引导了 VMware 的创立。现在是虚拟化技术市场的领导者。

本篇文章主要讲解 Disco 的底层实现技术,并理解虚拟化技术是如何工作的。

虚拟化 CPU

为了在一个 virtual machine monitor 之上运行一个虚拟机(包括操作系统和运行与操作系统之上的应用),最基础的技术就是 limited direct execution(限制指令执行),这种技术我们在介绍 CPU 虚拟化的时候也见过。

假设我们现在在单处理器上运行多个虚拟机,我们要切换不同操作系统上的应用程序。

VMM 必须要承担起 Machine Switch 的工作,因为 VMM 必须把整个虚拟机操作系统的状态都得保存下来(寄存器,程序计数器,特权的硬件状态),然后把另一个虚拟机的状态给恢复到硬件上,然后设置程序计数器为准备运行的虚拟机的程序计数器,就完成了一次切换。

准备运行的虚拟机的程序计数器此时可能指向的是内核代码(操作系统本身),也可能是用户态的程序指令。

这就是有点 trick 的地方了,当虚拟机操作系统执行某些特权指令(privileged operation)的时候。比如说,操作 TLB,切换进程之类的。

在虚拟环境中,操作系统不能直接执行特权指令,否则的话,虚拟机操作系统将有无限的权利,它应该在 VMM 之上,VMM 必须能在某种地方知道且拦截虚拟机操作系统的特权指令的执行,然后再把执行流交还给虚拟机。

举个例子,下面的汇编代码打算打开一个文件,在应用层面就是发起一个 open(char *path, int flags, mode_t mode) 的系统调用,

open:
    push    dword mode
    push    dword flags
    push    dword path
    mov     exa, 5
    push    eax
    int     80h

现在在栈中保存参数,然后通过指令 int 80h 发起系统调用,此时执行流会走到操作系统的预定义代码中,控制权交给内核。这一切都是由 int 指令发起的。这其实就是 trap handler

01

操作系统启动的时候,会设置好这些 trap handler 放在哪里,当要执行系统调用了,操作系统得知道去哪里找到对应的代码执行。同时,操作系统会将硬件从用户态切换到内核态,这时候操作系统的权限是非常大的(为什么要切换,因为操作系统相信自己的代码是没错的,它要控制用户的访问,不让用户写的程序随便的访问硬件资源),可以说拥有绝对无上的权利,整个硬件资源都在它的掌控之中。

而在一个虚拟化平台之中,事情变得有点不一样。

当一个虚拟机操作系统执行一个系统调用的时候,它做的事情和之前的一样(不让它感知到自己在虚拟化环境中),不同的地方在于 VMM 能感知到这个系统调用的发生(借助硬件的帮助),而且知道该虚拟机操作系统要执行的 trap handler 在哪里(通过虚拟机操作系统启动时候记录下来)。

当虚拟机 OS 发出了系统调用的特权指令,VMM 感知到了就跳转到该操作系统对应的 trap handler,而当 trap handler 执行完毕通过执行 ret 指令返回用户态的进程指令的时候,这时候也会触发 VMM 的感知,然后 VMM 再把 CPU 的执行流交还回虚拟机 OS 上的进程。

02

通过上图的对比可以看到,我们多执行了很多指令,大胆的猜测这些额外的消耗可能会影响到性能。

那么问题来了,虚拟机 OS 具体是运行在哪个模式上呢,通过上面的描述我们看既不是 kernel mode,肯定也不是 user mode?

其实是一直硬件提供的中间状态,less privileged mode

在 Disco 的工作中,Rosenblum 在 MIPS 处理器上实现的这种模式叫做 supervisor mode。有没有联想到一个开源的进程管理软件 supervisor,哈哈!

When running in this mode, one still doesn’t have access to privileged instructions, but one can access a little more memory than when in user mode; the OS can use this extra memory for its data structures and all is well.
On hardware that doesn’t have such a mode, one has to run the OS in user mode and use memory protection (page tables and TLBs) to protect OS data structures appropriately.

其实硬件也没有这么一个模式,换句话说,可能是监测了硬件的执行指令,然后加上了 VMM 的功能逻辑。

虚拟化内存

通常来说,每个操作系统都把物理内存看做了连续的页。以页为单位分配给它自身或者用户进程。并且通过虚拟地址转换和磁盘等辅助内存给进程提供一个能使用的更大的内存空间的抽象(虚拟化)。

在虚拟化中,虚拟机操作系统看到的“物理”内存其实是在 VMM 管理的 机器内存(machine memory) 之上的一层抽象。因此我们得另外加一层映射,操作系统通过页表把虚拟地址转化成“物理”地址,而 VMM 通过每个操作系统独立的一个页表把“物理”地址映射成为更为底层的机器地址。

03

通常情况下,操作系统会负责处理虚拟地址的翻译过程,具体是指 TLB Miss 的部分,这部分由操作系统特定的 trap hanlder 负责的,这涉及到 操作系统要执行特权指令,CPU 从用户态切换到内核态等等。

04

在虚拟化环境当中,VMM 在虚拟化 OS 之下,事情又变得有点不一样~

05

虚拟化 OS 不是硬件的真正管理者,VMM 才是,因此 VMM 必须知道如何处理虚拟化 OS 的 TLB Miss,但是通常情况下,VMM 是不知道的,VMM 知道的是当发生 TLB Miss 的时候它能被通知到(the VMM gets notified when any non-privileged code tries to do something that is privileged, of course)。

这是一个 trick 的地方,VMM 存储的不是 VPN -> PFN 的映射,而是 PFN -> MFN(Machine Frame Number)。也就说当发生 TLB Miss 的时候,虚拟机 OS 已经通过页表查找到了 VPN 对应的 PFN 是什么了,这时候它要更新 TLB,然后 retry 那条内存访问的指令、触发 TLB Hit,此时 VMM 站了出来,当虚拟机 OS 要更新 TLB 的时候,拦截这条特权指令,根据 PFN -> MFN 这个映射关系,偷偷地把虚拟机操作系统设置的 PFN 替换成 MFN。

这也意味着 VMM 必须跟踪每一个运行着的虚拟化 OS 的 PFN -> MFN 的映射,这就像一个操作系统维护的“页表”一样。

那么这个“页表”存在哪里呢?替换策略是怎样的呢?

The Information Gap

书中是这么解释的,

Just like the OS doesn’t know too much about what application programs really want, and thus must often make general policies that hopefully work for all programs, the VMM often doesn’t know too much about what the OS is doing or wanting; this lack of knowledge, sometimes called the information gap between the VMM and the OS, can lead to various inefficiencies.
 
It makes sense to spin like this if the OS in charge of the entire machine and thus knows there is nothing else that needs to run. However, when a VMM is running underneath two different OSes, one in the idle loop and one usefully running user processes, it would be useful for the VMM to know that one OS is idle so it can give more CPU time to the OS doing useful work.

这有利于资源的更有效利用吧。

Comments
Write a Comment