最近几年随着并行计算、集群等技术的火热,虚拟机领域焕发了第二春。虚拟化就是指多台虚拟机共享一台物理机硬件的计算机体系结构技术。虚拟化的基本思想是分割软硬件以产生更好的系统性能(实际上,软硬件资源并没有增加,只是利用率提高了)。一个例子就是常见的“虚拟内存”,通过将一部分不用的磁盘写为页面文件,可以获得更大的内存地址空间,我们就可以跑更大的程序了。
1. 层次
我们都知道,传统方式是应用程序跑在操作系统上,而操作系统需要适应主机的特定体系结构,比如x86的机器上就只能跑win,linux,MaxOS等几种,AIX就没法跑了。
有了虚拟化之后,用于的应用程序由相应的客户操作系统管理,且1-n个客户操作系统可以独立于主机的操作系统,运行在同一个硬件上,且不需要适配硬件的特定体系结构。这通常通过增加一个虚拟化层来实现,该虚拟化层称为hypervisor或VMM(Virtual Machine Monitor)。
虚拟化层将主机的物理硬件资源虚拟为可被各虚拟机互斥使用的虚拟硬件资源,这可以在不同的层面实现,如下图:
层次 | 例子 |
应用程序级 | JVM/.Net CLR |
库支持(用户级API)级 | WINE |
操作系统级 | Docker |
硬件抽象级 | VMware/Xen/KVM |
指令集体系结构级 | Bochs |
我们从下往上看。
指令集体系结构级:
也称为ISA(Instruction Set Architecture)级,通过使用物理主机的ISA模拟一个给定的ISA来实现。基本的模拟方式是“代码解释”,一个软件翻译层的程序将源指令逐条翻译为目标指令,一条源指令可能会对应上百条目标指令来实现相同的功能。为改进性能,出现了动态二进制翻译技术,将动态源指令的基本块转换为目标指令,基本块可以转化为超级块来进一步提升转换的效率。尽管如此,这种方式还是五种层次中效率最低的。
一个典型的代表是Bochs,可以在各种Unix like系统中模拟x86平台,包括指令集、I/O、内存、BIOS等都可以模拟。一些爱折腾的大仙在Android上装windows就是用了这种方式(一个例子见 http://bbs.hiapk.com/thread-4750312-1-1.html
),当然速度惨不忍睹就是了。
硬件抽象级:
该类虚拟化直接在原始硬件上进行。该方法虚拟CPU、内存和I/O设备,目的是通过多个并行用户来改进硬件资源的利用率。该类的典型代表是常用的VMware和Xen。这个层级的虚拟化有全虚拟化、半虚拟化等方式,参见第2部分介绍。
硬件级别的第一个问题是CPU虚拟化。如果当VMM/hypervisor运行在管理模式时,CPU支持在用户模式运行虚拟机的特权指令和非特权指令,则该CPU体系结构是可虚拟化的。RISC的所有控制敏感指令和行为敏感指令都是特权指令,因此RISC CPU是天然可虚拟化的(指这些指令都会自动陷入hypervisor)。而x86体系结构并不是为虚拟化设计,如果套用全虚拟化、半虚拟化等方式,则效率较低。为此提出了一种“硬件辅助的CPU虚拟化”特殊处理,引入一种特殊的运行模式和指令,使得VMM和操作系统可以运行在不同模式中。在Intel和AMD的x86处理器中,这种模式称为特权模式(位于环1),于是操作系统仍运行在环0(x86中操作系统只能跑在环0,见http://baike.baidu.com/link?url=sxY1XeWXddheQHsPnAOLIUpnHF6VWLCrw1IYv67T3BPaLAcnb5-j0NQpdY_B0D8iIFmUIh6OnENGyBAjsEgR1q),hypervisor运行在环1,所有特权指令和敏感指令都会自动陷入到hypervisor中。
通常来讲硬件辅助虚拟化应具有更高的效率,然而,由于从hypervisor到客户操作系统需要在处理器模式之间切换(用户模式和特权模式),会引起较高的开销,有时并不会优于二进制翻译。因此,如VMware等现在使用混合的方法,一部分任务交给硬件,其余则仍由软件处理。
内存虚拟化则类似于现代操作系统的虚拟内存。不同的是虚拟内存只有一级映射,而内存的虚拟化需要客户操作系统和VMM分别维护“虚拟内存——物理内存”和“物理内存——机器内存”的映射,共两级映射。VMware使用影子页表进行虚拟内存到机器内存的地址转换。但该技术效率太低(是早期VMware巨慢的原因之一),Intel开发了基于硬件的EPT(扩展页表)技术来加以改进,下图为该技术的示意图(盗的台巴子的,原文找不到了)。(AMD也有类似的技术称为NPT)
I/O虚拟化包括管理虚拟设备和共享的物理硬件之间I/O请求的路由选择。有如下三种方式:全设备模拟、半虚拟化和直接I/O,我也不是很懂就不展开讲了。只知道VMware用的是全设备模拟。
该类虚拟化由于可以得到CPU级别的支持,性能是比较高的,但是VMM仍然需要自己实现调度器、内存管理器等部件,因此不论VMware还是Xen的代码都比较庞大。
操作系统级:
指处于传统操作系统和用户应用程序之间的抽象层。操作系统级虚拟化在一个单一的物理服务器上创建隔离的容器和操作系统实例,常被用来创建虚拟主机环境,在大量互斥的不信任用户之间分配硬件资源。这种虚拟机也称为VE(虚拟执行环境)、VPS(虚拟专用系统)或容器。VE有自己的进程、文件系统、用户账号、IP地址、路由表、防火墙规则及其他设置。尽管VE可为不同用户分别定制,但它们仍共享同一个操作系统。
操作系统级虚拟化方案解决了硬件级虚拟化的很多问题:首先物理机器和虚拟机实例数可以动态改变;其次硬件级虚拟化的虚拟机初始化很慢,而操作系统级虚拟化几乎不需要时间;第三就是硬件全虚拟化性能较低,半虚拟化又需要修改客户操作系统(全虚拟化和半虚拟化后面讲),但操作系统级虚拟化就几乎没有开销。以上几个特性使操作系统级虚拟化非常适合云计算场景。
一个典型的代表是Docker,其基于Linux的LXC,说白了就是cgroups。Docker最近两年火得不要不要的,但实际上不是新鲜事物,而且一旦用上Docker就需要传统的运维体系有巨大的变革,因此在大企业中很少见到用得好的(这个话题大了,以后再详细讲)。Docker的优点就是上述操作系统级虚拟化的优点,缺点也很明显,首先宿主机和虚拟机都只能是linux,资源隔离比VMware这些也有差距,cpu和disk的管理较简单。(当然在Docker间共享数据还是很容易的,用数据卷就行了,我不懂Docker都知道这玩意儿,上周一批懂Docker的砖家一起开会都没想到这东西,我也是醉鸟)
库支持级:
这种方案基于一个思路,大部分应用都是基于用户库API而不是系统级调用的,因此可以通过API钩子控制应用程序和其他系统部分之间的连接,使得带有库接口的虚拟化成为可能。这种方式和网游脱机外挂其实异曲同工,难度是不高的。
一个典型的代表是WINE,其可以在Unix like系统上运行win32应用程序。当然其实是非常难用的,高中刚开始玩linux那会儿,总想着用WINE跑win上的游戏,但是除了一些简单的exe可以运行,directx游戏都是跑不了的,原因是DLL依赖太复杂了,很多也不支持。这么多年过去了问题越来越恶化,毕竟微软几千号人写API,WINE的开发就那么几个,模拟得过来么。
应用程序级:
这个级别的虚拟化是将一个应用程序虚拟化为一个虚拟机。最流行的方法是高级语言虚拟机,在这种情况下,虚拟化层作为一个应用程序处于操作系统之上,并且这一层抽象出一个虚拟机,其可以运行为特定的机器环境所编写和编译的程序。
典型的代表是Java虚拟机和微软的.Net CLR,当然因为纯应用程序级虚拟化效率很低,两者现在都不是纯的虚拟机,比如JVM中很关键的一部分就是JIT(即时编译),当年Java1.0时代的纯解释执行才等同于应用程序级虚拟机,那个效率谁用谁知道。
以下是五种级别的对比,*越多越好(性能高,灵活性高,实现复杂度低,隔离性好):
实现级别 | 高性能 | 应用程序灵活性 | 实现复杂度 | 应用程序隔离性 |
应用程序级 | ** | ** | * | ***** |
库支持(用户级API)级 | *** | ** | **** | ** |
操作系统级 | ***** | ** | *** | ** |
硬件抽象级 | ***** | *** | * | **** |
指令集体系结构级 | * | ***** | *** | *** |
2.机制
可根据是否需要修改客户操作系统,分为硬件虚拟化和编译器支持虚拟化。硬件虚拟化又分为:全虚拟化和基于主机的虚拟化(很多材料将两者等同,是不对的)。编译器支持虚拟化只有一种,称为半虚拟化。
全虚拟化:
在全虚拟化中,非关键指令直接运行在硬件之上,而关键指令(特权指令、控制敏感指令、行为敏感指令)被替换为通过软件模拟的陷入VMM/hypervisor的指令。即,(非x86系统中)VMM运行在环0,客户操作系统运行在环1,用户应用程序运行在环3,用户应用程序的指令直接操作系统硬件,客户操作系统的指令通过VMM二进制翻译后操作系统硬件。见下图。VMware就使用了这种机制。
基于主机的虚拟化:
这种机制是,宿主机的操作系统仍旧负责管理硬件,在宿主机操作系统上安装一个虚拟化层,客户操作系统安装并运行在虚拟化层之上。可见这种机制下不论何种指令,都需要经虚拟化层转发后由宿主机操作系统执行,并且当客户操作系统的ISA与底层硬件的ISA不同时,还需要在虚拟化层做二进制翻译,因此效率是很低的。也可见这种方式和全虚拟化是有区别的,所以必须分开讲。
半虚拟化:
半虚拟化技术是后来才出现的技术,也叫做准虚拟化技术,现在比较热门,它就是在全虚拟化的基础上,把客户操作系统进行了修改,增加了一个专门的API,这个API可以将客户操作系统发出的指令进行最优化,即不需要VMM/Hypervisor耗费一定的资源进行翻译操作,因此Hypervisor的工作负担变得非常的小,整体性能也有很大的提高。不过缺点就是,要修改包含该API的操作系统,但是对于某些不含该API的操作系统(主要是windows)来说,就不能用这种方法。半虚拟化技术见下图。
这些都说完,就可以来比较另两种主流的虚拟化技术了:XEN和KVM。
XEN(参见 http://baike.baidu.com/link?url=J97539DMsA9HW2WZYsX7ZNliZUx9d2j3LarHw2yRd37Lh_ho6zAe1Gy5Xfh-z-TxuLH3Q6T2qfrNEBDmuMiAca)主要是为x86平台服务的,当运行修改的操作系统时可以使用半虚拟化机制,当CPU支持且操作系统无法修改时可使用全虚拟化机制。
KVM(参见http://baike.baidu.com/link?url=kV4fSyYs0tw-bhBpp6Fofh4C6c5nQDKmY1eTmIR-BPbliDsX0uYOF97ypPAsCqmsM96xt85ai4q89rO8XaEe8_,这里说KVM是全虚拟化,又是错的)不针对特定架构,但针对特定宿主操作系统,因为它是linux内核2.6.20后内置的,代码量比XEN少得多。当运行修改的操作系统时使用半虚拟化机制,当CPU支持时可使用改进的基于主机的虚拟化机制,即Hypervisor虽然位于主操作系统上,但在内核上打了个洞,如果CPU支持如Intel
VT之类的技术,虚拟机操作系统的关键指令可直接映射到物理硬件上,从而大大提升性能,如图所示(盗自 http://www.linuxidc.com/Linux/2015-03/114462.htm )。
如果硬要比较的话,在编译器支持虚拟化(半虚拟化)时XEN优于KVM,在硬件虚拟化时KVM优于XEN。
(本文参考《云计算与分布式系统》,但是原书的相关章节有些混乱,也有一些跟不上形势的地方,一并修改了)