虚拟机是怎么实现的

又是一个知乎问题,坑太深,记一篇

虚拟化(Virtualization)

随云的发展潮流,虚拟化技术在商业应用上的优势日益体现,降低 IT 成本,增强系统安全性和可靠性

十年前只是桌面用户测试其他操作系统的玩具?

大家都用过VMWare、VirtualBox 虚拟机 ?

只要计算机上同时运行着多个任务,就会有任务隔离的需求,虚拟化就是让每个任务看起来独占整个计算机、隔离任务之间影响的技术

名词

  • host machine,真实os
  • guest machine,被虚拟化后的OS
  • Hypervisor 又称虚拟机器监视器(virtual machine monitor, VMM)

type 1,type2

type 1 本地或裸机Hypervisor

vmm 直接运行在主机的硬件来控制硬件和管理guest os

  • 特点
  1. 需要硬件支持
  2. 虚拟机监视器作为主操作系统
  3. 运行效率高
  • 举例
  1. VMware5.5后
  2. Xen3.0以后版本
  3. Virtual PC 2005
  4. KVM

type 2: Hosted Hypervisor

vmm 运行在传统的os上,就像其他计算机程序那样运行

  • 特点
  1. 虚拟机监视器作为应用程序运行在主操作系统环境内

  2. 运行效率一般较类型I低

  • 举例
  1. VMware5.5前
  2. Xen3.0以前版本
  3. Virtual PC 2004

史前时代,硬件虚拟化

1964 年 IBM m44/44x 用专用硬件,以分时方式让多个 IBM 7044 以为独占所有硬件资源运行

开启“分页”这个重要概念(每个VM要用虚拟地址,就要一层虚拟地址到物理地址的映射)

在 “进程” 概念尚未被发明的年代,多任务操作系统和虚拟化技术事实上是难以分开的, “虚拟机” 就是一个任务,当时还没有 Intel x86 这种霸主地位的体系结构,各家的大型机各自为政,也谈不上兼容别家的体系结构

这种 “任务级” 或者说 “进程级” 虚拟化,从概念上延续到今天,就是以 LXC 和 OpenVZ 为代表的操作系统级虚拟化

从模拟执行到二进制翻译

大型机时代各大厂商有各自体系结构和指令集

为什么没有出现各种指令集之间的翻译软件呢?

思科靠兼容各家网络设备和协议起家的(八卦,那对创始人小情侣希望使用网络传递情书,但网络设备五花八门的,于是他们就发明了兼容多种协议的路由器)

指令集比网络协议麻烦的地方,冯·诺依曼体系结构中,数据和代码共享线性内存空间 ,能把动态生成的数据作为代码来执行

在二进制代码运行之前就做好指令集之间的静态翻译,是不可能的

最简单粗暴的解决方法是 “模拟执行”

开一个大数组当 “虚拟机” 的内存,拿来指令集手册,写一个无数个 case 的 switch 语句,判断当前要执行的是什么指令,按照手册的说法模拟执行。但效率至少比物理机慢一个数量级

“动态类型语言”, Python、PHP、Ruby,编译成中间代码后,大多也是用这种 “模拟执行” 的方法,快不起来

著名的 x86 模拟器 Bochs 就是模拟执行的,虽然慢,但兼容性好,而且不容易有安全漏洞

不能因为指令翻译难做就因噎废食,一个运行 1 亿次的 for 循环,里面如果都是加加减减一类的操作,翻译成机器码直接执行,肯定比模拟执行要快

在二进制翻译前面,要加上 “动态” 两个字,也就是在程序执行的过程中,能翻译的部分翻译成目标架构的机器码直接执行(并缓存起来以便重复利用),不能翻译的部分就陷入到模拟器里,模拟执行特权指令,再对后面的代码进行翻译。如今 Java、.NET、JavaScript 等使用的 JIT(Just-In-Time)技术也是类似的套路

如果只是虚拟相同的体系结构,比如在 64 位 x86 系统上虚拟一个 64 位 x86 系统,也需要做指令翻译

比如,虚拟机可能读取特权寄存器 GDT、LDT、IDT 和 TR,并不会触发处理器异常(即虚拟机管理器无法捕获到这样的行为),但这些特权寄存器的值又是需要对虚拟机 “伪造” 的。这就需要在虚拟机读取特权寄存器的指令执行之前,把它替换成调用虚拟机管理器的指令

在大型机年代,能把动态二进制翻译这件事做好的天才级人物 Fabrice Bellard 才刚刚出生(1972 年)

QEMU 是目前最流行的采用动态二进制翻译技术的虚拟化软件。可以模拟 x86、x86_64、ARM、MIPS、SPARC、PowerPC 等多种处理器架构,无修改地运行这些架构上的操作系统

另还有 ffmpeg,不展开

Intel x86 架构为例,四个特权级 0~3

一般 操作系统内核(特权代码)运行在 ring 0(最高特权级),用户进程(非特权代码)运行在 ring 3(最低特权级)

使用了虚拟机之后,虚拟机(Guest OS)运行在 ring 1,主机操作系统(VMM,Virtual Machine Monitor)运行在 ring 0

比如在 Windows 上装个 Linux 虚拟机,Windows 内核运行在 ring 0,被虚拟的 Linux 内核运行在 ring 1,Linux 系统里的应用程序运行在 ring 3

当虚拟机系统需要执行特权指令时,虚拟机管理器(VMM)就会立即捕获它并模拟执行这条特权指令,再返回到虚拟机系统

为了提高系统调用、中断处理的性能,有时会利用动态二进制翻译的技术,在运行前把这些特权指令替换成调用虚拟机管理器 API 的指令。如果所有特权指令都模拟得天衣无缝,虚拟机系统就像运行在物理机器上一样,完全不能发现自己运行在虚拟机里。(事实上还是有一些破绽的)

请虚拟机系统和 CPU 来帮忙

动态二进制翻译虽然比模拟执行快了很多,但由于每条特权指令都要到虚拟机管理器里绕一圈(模拟执行),离物理机的性能仍然有不小的差距

两种要让虚拟机快起来的方法:

  1. 让虚拟机操作系统帮忙, “半虚拟化”(paravirtualization)或 OS-assisted virtualization
  2. 让 CPU 帮忙, “硬件辅助虚拟化”(hardware-assisted virtualization)

这两种方法不是互斥的。现代很多虚拟化解决方案,如 Xen、VMware,都同时使用了两种方法

半虚拟化(paravirtualization)

动态二进制翻译难点和性能瓶颈在模拟执行杂七杂八的特权指令

修改虚拟机系统的内核,把那些特权指令改得好看些

在多数情况下,并不需要对虚拟机刻意 “隐瞒” 虚拟化层的存在,而是要在虚拟机之间提供必要的隔离,同时又不造成太多性能开销

虚拟机系统与虚拟化层(主机系统)不再是严格的上下级关系,而是互信合作的关系,虚拟化层要在一定程度上信任虚拟机系统。在 x86 架构中,虚拟化层(Virtualization Layer)和虚拟机系统的内核(Guest OS)都运行在 ring 0

虚拟机系统的内核需要经过特殊修改,把特权指令改成对虚拟化层 API 的调用

现代操作系统中,由于这些体系结构相关的特权操作都被封装起来了(例如 Linux 内核源码中的 arch/ 目录),比起二进制翻译需要考虑各种边角情况,这种对虚拟机内核源码的修改就简单一些了

相比使用二进制翻译的全虚拟化(full virtualization),半虚拟化是牺牲了通用性来换取性能,因为任何操作系统都可以无修改地运行在全虚拟化平台上,而每个半虚拟化的操作系统内核都要经过人肉修改

硬件辅助的虚拟化

同样的功能,专用硬件实现几乎总是比软件实现更快,几乎是一条金科玉律

在虚拟化这件事上,比尔搞不定的事情,自然也要请安迪来帮忙。(比尔是微软公司创始人,安迪是 Intel 公司创始人)

  1. 虚拟机里的程序不能访问未分配给它的资源
  2. vmm需要能够收回已经分配给虚拟机的资源

早期 x86 指令集不满足上述条件,需要vmm做复杂的动态二进制翻译

二进制翻译主要开销在 “捕获” 虚拟机系统的特权指令上,CPU 能做的事就是帮忙把 “捕获” 的事情给做了

Intel 方案 Virtual Machine Control Structures (VT-x)

在原有四个特权级基础上,增加一个专供虚拟机管理器(VMM)使用的 “Root Mode”

虚拟机系统尽管运行在 ring 0,其执行的特权指令仍然会被 CPU 自动捕获(触发异常),陷入到 Root Mode 的虚拟机管理器里,处理后再使用 VMLAUNCH 或 VMRESUME 指令返回到虚拟机系统

为了方便半虚拟化(paravirtualization)中用 API 调用替代 CPU 异常捕获,Intel 还提供了从虚拟机(Non-root Mode)到虚拟机管理器(Root Mode)的 “系统调用”:VMCALL 指令

Docker

Docker 基于 LXC,属进程级虚拟化方案,从宿主机看,每个运行的 Docker 都是一个进程

Xen/OpenVZ 是系统级虚拟化方案

一个亚马逊EC2 512MB内存单核的云主机开5个docker无压力,要是跑5个vmware那可费劲了

64位Linux上,Docker没有用虚拟机,所以性能很好

64位的Linux系统以外的系统上(Windows,Mac OS X),Docker利用了虚拟机技术

ref

虚拟机是怎么实现的?

https://www.zhihu.com/question/20848931

虚拟化技术大观

https://ring0.me/2014/12/virtualization-overview/

虚拟化技术漫谈

https://www.ibm.com/developerworks/cn/linux/l-cn-vt/