19.1 Why Virtual Machine?

今天讨论的话题是虚拟机。今天的内容包含三个部分:

  • 第一个部分是Trap and Emulate,这部分会介绍如何在RISC-V或者QEMU上构建属于自己的Virtual Machine Monitor(注,有些场合也称为Hypervisor)。

  • 第二部分会描述最近在硬件上对于虚拟化的支持。

  • 最后是讨论一下今天的论文,它使用了第二部分中硬件上的支持。

首先什么是虚拟机?你可以认为这是对于计算机的一种模拟,这种模拟足够能运行一个操作系统。QEMU可以认为是虚拟机的一个例子(注,QEMU应该是属于VMM/Hypervisor)。

在架构的最底层,位于硬件之上存在一个Virtual Machine Monitor(VMM),它取代了标准的操作系统内核。VMM的工作是模拟多个计算机用来运行Guest操作系统。VMM往上一层,如果对比一个操作系统的架构应该是用户空间,但是现在是叫做Guest空间。所以在今天的架构图里面,上面是Guest空间,下面是Host空间(注,也就是上面运行Guest操作系统,下面运行VMM)。

在Guest空间,会有一个或者多个Guest操作系统内核,或许其中一个是Linux kernel。这里的Linux kernel会觉得自己就是个普通的内核,并在自己之上还运行一堆用户进程,例如VI,C Compiler。我们或许还有另一个Guest运行了Windows操作系统,同时也包含了Windows用户进程。所以,在Host空间运行的是VMM,在Guest空间运行的是普通的操作系统。除此之外,在Guest空间又可以分为Guest Supervisor Mode,也就是Guest操作系统内核运行的模式,和Guest User Mode。

VMM的主要目的是提供对计算机的模拟,这样你可以不做修改就启动普通的Linux,普通的Windows系统,并运行在虚拟机内,并且不用担心任何奇怪的事情发生。所以,VMM必须要能够完全按照实际硬件的行为来模拟Guest Supervisor Mode和Guest User Mode,尽管实际上不可能完全一样,我们之后会讨论VMM对于这两种模式的模拟。

那么人们为什么会想要使用虚拟机呢?实际中有很多原因使得人们会在一个计算机上运行多个相互独立的操作系统。在一个大公司里面,你需要大量的服务,例如DNS,Firewall等等,但是每个服务并没有使用太多的资源,所以单独为这些服务购买物理机器有点浪费,但是将这些低强度的服务以虚拟机的形式运行在一个物理机上可以节省时间和资金。

虚拟机在云计算中使用的也非常广泛。云厂商,例如AWS,不想直接出借物理服务器给用户,因为这很难管理。它们想向用户出借的是可以随意确定不同规格的服务器。或许有两个用户在一台物理服务器上,但是他们并没有太使用计算机,这样AWS可以继续向同一个物理服务器上加入第三或者第四个用户。这样可以不使用额外的资金而获得更高的收益。所以,虚拟机提供了额外的灵活性,这里借助的技术是:将操作系统内核从之前的内核空间上移至用户空间,并在操作系统内核之下增加新的一层(注,也就是虚拟机的内核是运行在宿主机的用户空间,虚拟机的内核通过新增加的一层VMM来对接底层硬件)以提供这里的灵活性。

还有一些其他的原因会使得人们使用虚拟机。第一个是开发内核,这就是为什么我们在课程中一直使用QEMU。能够在虚拟环境而不是一个真实的计算机运行XV6,使得这门课程对于你们和我们来说都要方便的多。同时对于调试也更容易,因为相比在物理计算机上运行XV6,在QEMU提供的虚拟机环境中运行可以更容易的提供gdb的访问权限。

最后一个人们使用虚拟机的原因是,通过新增的VMM提供的抽象可以实现更多的功能。例如,你可以为整个操作系统和其中的用户进程做一个快照,并在磁盘中保存下来。稍后再恢复快照,并将操作系统和其中的用户进程恢复成做快照时的状态。这可以增加运行的可靠性,或者用来调试,或者用来拷贝虚拟机的镜像并运行多次。除此之外,还可以将一个Guest操作系统迁移到另一个计算机上。如果你在一个物理计算机上运行了一个Guest操作系统,现在需要关闭并替换该物理计算机,你可以在不干扰虚拟机运行的前提下,将它迁移到另一个物理计算机,这样你就可以安全的关闭第一个物理计算机。

以上就是人们喜欢使用虚拟机的原因。虚拟机实际上应用的非常非常广泛,并且它也有着很长的历史。虚拟机最早出现在1960年代,经过了一段时间的开发才变得非常流行且易用。

对于这们课程来说,我们之所以要学习虚拟机是因为VMM提供了对于操作系统的一种不同视角。在操作系统的架构中,内核之上提供的封装单元(注,视频中说的是container,但是container还有容器的意思,所以这里说成是封装单元)是我们熟悉的进程,内核管理的是多个用户进程。而在VMM的架构中,VMM之上提供的封装单元是对计算机的模拟。VMM的架构使得我们可以从另一个角度重新审视我们讨论过的内容,例如内存分配,线程调度等等,这或许可以给我们一些新的思路并带回到传统的操作系统内核中。所以,在虚拟机场景下,大部分的开发设计研究工作,从传统的内核移到了VMM。某种程度上来说,传统操作系统内核的内容下移了一层到了VMM。

今天课程的第一部分我将会讨论如何实现我们自己的虚拟机。这里假设我们要模拟的是RISC-V,并运行针对RISC-V设计的操作系统,例如XV6。我们的目的是让运行在Guest中的代码完全不能区分自己是运行在一个虚拟机还是物理机中,因为我们希望能在虚拟机中运行任何操作系统,甚至是你没有听说过的操作系统,这意味着对于任何操作系统的行为包括使用硬件的方式,虚拟机都必须提供提供对于硬件的完全相同的模拟,这样任何在真实硬件上能工作的代码,也同样能在虚拟机中工作。

除了不希望Guest能够发现自己是否运行在虚拟机中,我们也不希望Guest可以从虚拟机中逃逸。很多时候人们使用虚拟机是因为它为不被信任的软件甚至对于不被信任的操作系统提供了严格的隔离。假设你是Amazon,并且你出售云服务,通常是你的客户提供了运行在虚拟机内的操作系统和应用程序,所以有可能你的客户运行的不是普通的Linux而是一个特殊的修改过的Linux,并且会试图突破虚拟机的限制来访问其他用户的虚拟机或者访问Amazon用来实现虚拟机隔离的VMM。所以Guest不能从虚拟机中逃逸还挺重要的。Guest可以通过VMM使用内存,但是不能使用不属于自己的内存。类似的,Guest也不应该在没有权限的时候访问存储设备或者网卡。所以这里我们会想要非常严格的隔离。虚拟机在很多方面比普通的Linux进程提供了更加严格的隔离。Linux进程经常可以相互交互,它们可以杀掉别的进程,它们可以读写相同的文件,或者通过pipe进行通信。但是在一个普通的虚拟机中,所有这些都不被允许。运行在同一个计算机上的不同虚拟机,彼此之间是通过VMM完全隔离的。所以出于安全性考虑人们喜欢使用虚拟机,这是一种可以运行未被信任软件的方式,同时又不用担心bug和恶意攻击。

前面已经指出了虚拟机的目标是提供一种对于物理服务器的完全准确的模拟。但是实际中出于性能的考虑,这个目标很难达到。你将会看到运行在Guest中的Linux与VMM之间会相互交互,所以实际中Linux可以发现自己是否运行在VMM之上。出于效率的考虑,在VMM允许的前提下,Linux某些时候知道自己正在与VMM交互,以获得对于设备的高速访问权限。但这是一种被仔细控制的例外,实现虚拟机的大致策略还是完全准确的模拟物理服务器。

Last updated