分类: 虚拟化
2019-02-20 13:58:59
引言
现代的计算机系统是极其复杂的机器系统,管理计算机系统复杂性的关键技术之一是用良好的界面将其划分成若干抽象层。这些抽象层的存在可以使底层的细节被忽略或者简化,这样可以使高层的设计带来简化。一个简单的计算机抽象层次如图1 所示。这些抽象层组成一个层状结构,底层使用硬件,高层使用软件实现。在硬件层,所有的部件都是物质的,有着真实的属性。这些组件之间定义了接口,以便他们能够在物理上互相连接。在物理之上的各层中,部件是逻辑上的,它们没有物理属性。广义上说,计算机硬件之上的每一层对于其上层来说都是虚拟机。
图1 计算机抽象层次图
计算机上的软件是由一个机器系统来执行的。从操作系统角度看,如图2,整个机器系统是由底层来支撑的,系统可见的是指令集系统,底层的硬件可能包括一个或多个处理器、内存、输入输出设备等。这个系统是一个完整的执行环境,这个环境可以支持若干个进程,这些进程共享一个文件系统以及其他的输入输出设备。这个系统申请物理内存和输入输出这一进程使用,并且允许进 程通过操作系统与这些资源交互。所以从系统观点看,机器就是由底层硬件构成的,指令集系统提供了该系统和机器之间的接口。但是机器这个词语不只是可以用来描述计算机的硬件结构。从进程的角度看,如图3 ,进程可见的是应用程序二进制接口,机器由内存地址空间和用户级指令构成。IO设备是通过操作系统来访问的,并且进程访问输入输出设备的唯一方式是通过操作系统调用。所以从进程的角度看,机器就是操作系统和用户可见的硬件。应用程序二进制接口提供了进程和机器之间的接口,它包括操作系统调用和访问硬件的部分指令。
图2 指令集
图3 应用程序二进制接口
虚拟机是在真实机器之上的一个软件实现,该实现能够支持被虚拟的机器的体系结构。虚拟机技术广泛地被操作系统开发者、程序语言设计者、编译器设计者和硬件设计者所采用。虚拟机能够像被它虚拟的机器一样执行软件,这个软件可能是一个进程或者一个完整的系统,这取决于虚拟机的类型。虚拟机可以拥有底层真实机器不同的资源,这种不同可以在数量或者类型上不同。例如,虚拟机器可能比底层真实机器拥有数目不一样的处理器 并且这些处理器能够执行不同的指令集。通常,构造虚拟机的过程需要首先考虑两个方面:1.虚拟资源或者状态到真实资源的映射2.用底层的机器指令或系统调用实现虚拟机的指令或者系统调用。
在一个完整的虚拟机系统中,底层的平台称作宿主平台,运行在操作系统级虚拟机之上的软件称为客户,虚拟机本身模拟的平台称为目标平台。如图4所示,对客户来说,底层的虚拟机软件在宿主平台上模拟了目标平台,这样原来只能在目标平台上运行的客户就能间接在宿主平台上运行。
图4 虚拟机系统
从虚拟机在计算机系统中所处的位置和设计目的看,可以把虚拟机分为硬件虚拟机、应用程序虚拟机和操作系统级虚拟机。硬件虚拟机主要提供模拟真实的硬件的能力。应用程序虚拟机提供支持单独的进程的能力,位于应用程序二进制接口层,在操作系统和硬件混合层之上,模拟用户层指令和操作系统调用。操作系统级虚拟机提供执行用户级程序的能力,一般由能实现操作系统级虚拟机的软件来构建。操作系统级虚拟机能够将一个真实的服务器划分成若干个虚拟的服务器,每个服务器看起来都是一个功能基本完整的服务器,能运行独立的操作系统,并且能够独立地重新启动,所有的虚拟机共享同一个操作系统核心。
高级语言虚拟机(Language-level Virtual Machines)
应用程序虚拟机提供给用户一个虚拟的应用程序二进制接口环境,也称作高级语言虚拟机。它能够使应用程序与平台相对独立。因为可以开发针对不同平台的虚拟机版本,所以任何针对该虚拟机的应用程序都能在实现了该虚拟机的平台上运行,而不是为具体的应用程序开发出针对众多平台的版本。应用程序在虚拟机上的运行可以采用解释或即时编译的方式。
通过设计一个完整而通用的运行时框架可以实现较好的跨平台可移植性。在设计一个进程级虚拟机的同时,定义好一个应用程序开发环境便可以达到这个目的。这个虚拟机环境不直接对应于任何一种真实的平台,但它符合应用程序开发的高级语言特性,并有很好的可移植性。这种应用程序虚拟机与进程虚拟机很相似,但它尽可能地减少与硬件和操作系统相关的特性,因为这些特性降低了平台无关性。
应用程序虚拟机是随着Pascal语言环境而流行起 来的。在传统的编译系统中,如图5,包含一个前端和代码生成器,它执行词法、语法、语义分析,以得到一种简单的中间代码,这种中间代码不与机器码相似但比机器码更为抽象,这种中间代码不包含具体的寄存器分配。代码生成器则把中间代码转换成二进制代码,这种二进制代码就包含了某种特定的指令集和与操作系统相关的机器码。这个二进制文件便能够在支持该种指令集和操作系统的平台上执行。为了能让程序能够在另外一种平台上执行,必须将源程序编译成另外一个平台的二进制代码。
图5 编译器发布的流程
在虚拟机系统下开发,则过程稍有不同,主要是程序的 发布的时机不同。如图6,一个经典的编译器前端产生中间形式的抽象机器码。在应用程序虚拟机中,这里产生的是基于栈的指令集代码。这种虚拟指令集本质上就是虚拟机的机器代码。这种可移植的虚拟指令集代码可以发行到不同的平台上执行。对于每一个平台,能够执行这种虚拟指令集的虚拟机都需要被实现。以简单的形 式来说,这个虚拟机包含一个解释器,能够取指令、译码、执行对应的状态转换(可能涉及内存和栈操作)。输入输出功能则是通过作为该虚拟机一部分的标准库来 实现。对于高性能的虚拟机来说,虚拟机代码可以转换成宿主机机器码,以便在宿主平台上直接运行。
图6 虚拟机发布的流程
应用程序虚拟机可以使软件移植性变得容易。虽然实现虚拟机需要付出一些成本,但是与在每一个平台上开发一个编译器并且将所有应用程序重新编译相比,这仍然是一个相对简单的工程,甚至也比为一个典型的真实指令集开发一个进程虚拟机更简单。
SUN公司的JAVA虚拟机和微软公司.NET 框架的通用语言接口是广泛使用的应用程序虚拟机的例子。平台无关性和安全性是JAVA虚拟机和通用语言接口的核心。这两种系统的指令集都是基于字节码的,也就是说,指令被解码为字节序列,每个字节是一个操作码,一个单字节操作数或者是多字节操作数的一部分。这些字节码指令集是基于堆栈的,并且有抽象数据规范和内存模型,而且内存大小在理论上是无限的,因为垃圾收集通常是实现的一部分。因为任何一种硬件平台都可能是执行JAVA或者基于通用语言接口程序的平 台,所以程序不会编译成面向特定操作系统的代码,同时,一个标准库会是整个执行环境的一部分。其它的应用程序虚拟机有Forth virtual machine、Glulx、Inferno、Low Level Virtual Machine (LLVM)、Lua、Adobe Flash Player、MMIX、O-code machine、p-code machine、Parrot、Perl virtual machine、Portable.NET、Python virtual machine、ScummVM、、、、、、、、Waba、、等。
例子:
下面介绍一下我在2007年8月份做的一个虚拟机,整体功能模块如图7
图7:Jack(类似于Java的面向对象编程语言)模型
程序流程如下:
1. 高级语言:首先由程序员用高级语言编写程序
2. 中间代码:将用高级语言编写的程序文本通过高级编译器转化成JVM虚拟机能够理解的中间代码。
3. 汇编语言:通过JVM虚拟机,将中间代码转化成Hasm语法格式的汇编语言
4. 机器指令:通过Hasm汇编器,将汇编语言转化成机器指令
这个是Jack(类似于Java的面向对象编程语言,但是缺少继承,多态等属性)的架构模型,具体可以参见《》和本书的配套代码,具体不再累述了。
硬件虚拟机(system virtual machine, hardware virtual machine)
硬件虚拟机支持在一台计算机上存在多个运行时环境,每个运行时环境可以支持一个操作系统。硬件虚拟机能够使为某种操作系统编写的程序在另外一种操作系统上运行,或者提供比单一操作系统上多进程之间更严格的运行时沙箱。硬件虚拟机现在也被称作虚拟化或者虚拟服务器。能提供硬件虚拟机功能的宿主软件也被称作虚拟机监控器或虚拟机管理程序。
硬件虚拟机软件主要有三种实现方式:仿真,整系统模拟,动态翻译
虚拟机一般分为下面三种类型:
全虚拟化。虚拟机模拟完整的硬件,使一个未修改的操作系统可以再在一种完全不同的微处理器上运行。Bochs、DosBox、、、、、7 for Mac。
半虚拟化。虚拟机管理器提供一个应用程序编程接口供修改后的操作系统使用,能让一个经过修改的操作系统在一种完全不同的微处理器上运行。这个应用程序编程接口是一个抽象的整合硬件功能的接口。、Logic Domains、、、。
本地虚拟化。虚拟机模拟部分硬件,使未修改的操作系统能够在隔离状态下运行,但需要该操作系统与底层硬件使用相同类型的微处理器。本地虚拟化用软件方式模拟底层硬件不支持的指令,但最新的Intel已经支持硬件辅助的本地虚拟化。这种虚拟机的实例有VMware公司64位x86操作系统虚拟化和Virtual Iron公司的32位和64操作系统的虚拟化、。
对硬件虚拟机来说, 一个具有挑战的问题是支持与宿主机硬件上执行的指令集不同的指令集,比如在一种指令集硬件系统之上仿真另一种指令集。
最直接的仿真方法是解释。解释程序执行目标指令集的取指令来仿真每一个源指令集中的指令。这是一个相对较慢的过程,解释一条源指令可能需要几十条目标指令。
二进制翻译经常被用来提高性能。二进制翻译是把一段源指令转换成一段目标指令并执行同等的功能。翻译过程本身可能会有相当大的开销,但是如果一段源指令被翻译后的目标指令能够被缓存下来,以后重复执行该段源指令就会比解释执行快。因为二进制翻译是进程级虚拟机最重要的特性,所以有时进程级虚拟机被称为动态二进制翻译器。
解释和二进制翻译有不同性能。解释有着相对较低的启动开销,但指令的每一次模拟执行都要消耗一定的时间。二进制翻译有着较高的启动开销,但每次重复进行会比较快。因此,有些虚拟机使用了经过变形的分段仿真策略,例如,根据程序的行为收集统计信息。开始,一些指令是解释执行的,同时记录下那些经常被执行的指令序列。这些经常被执行的序列使用二进制翻译。一些系统可能会对执行频率非常高的翻译指令做额外的优化。在绝大多数虚拟机执行一段代码中,翻译和二进制翻译都会使用到。
系统虚拟机为多个进程共存提供一个完整的系统环境。 这种虚拟机在20世纪六十年代末七十年代初被开发出来,这些虚拟机也是虚拟机这个术语的来源。使用系统级虚拟机技术,能够使一个宿主硬件平台同时支持多种客户操作系统。在20世纪六七十年代,大型机非常庞大并且价格昂贵,计算机通常被大量的用户共享。不同的用户群可能希望在这个共享的硬件上运行不同的操作系统,而虚拟机刚好能满足这个需求。但随着工业的发展,硬件变得越来越便宜,桌上电脑也越来越普及,对于系统虚拟机的需求也越来越少。
但是近来,系统虚拟机正受到更多人的关注。那种庞大的昂贵的大型机现在被作为服务器和服务器群,这些服务器可能被很多用户共享使用。系统虚拟机最大的特点可能是他们提供多种操作系统的同时在一个硬件平台上运行的安全机制。在一个客户系统上运行的软件与另一个客户系统上的软件是完全隔离的,当一个客户系统的安全性受到威胁或者遭受灾难,运行在另一个客户系统上的软件不会受到影响。同时支持多种操作系统的能力可能不被大多数用户来说也是一个很重要的吸引力,例如同时支持Windows和Linux, 如图8。
对系统虚拟机来说,平台复制是虚拟机管理器的重要功能,而关键问题是在多个客户操作系统之间分配唯一的底层硬件资源。虚拟机管理器管理着硬件资源,所有的客户操作系统及其上的应用程序都需要经过虚拟机管理器的控制。当执行某些指令,特别是涉及到的共享硬件资源的指令,会通过虚拟机控制器解释这些指令,检查指令的正确性并执行相关操作,客户软件不会感觉到虚拟机控制器的操作。下面介绍两种比较重要的实现方式。
系统虚拟机的第一种实现方式,是虚拟机管理器放在硬件上,虚拟机放在虚拟机管理器之上,如图8所示。虚拟机管理器以最高权限模式运行,所以虚拟机管理器能够解释并实现所有客户操作系统的指令。从某些方面来说,这种虚拟机架构是最有效的,它以几乎相同的方式支持所有的客户操作系统。而缺陷就是,至少对于桌上电脑用户来说,安装虚拟机管理器之前必须完全擦除现有的系统,并且在安装虚拟机管理器之后安装客户操作系统。另一个缺陷,是需要提供针对虚拟机管理器的输入输出设备驱动,因为虚拟机管理器直接和输入输出设备交互。
图8 虚拟机位于硬件层之上
另一种如图9所示,虚拟机管理器放在已存在的宿主操作系统之上,这个虚拟机也被称为宿主虚拟机。这样,安装虚拟机的过程就类似于安装一个典型的应用程序的过程。并且,虚拟软件能够利用宿主操作系统提供设备驱动和其他基本服务。而缺陷就是,这样会带来一些性能的损失,因为操作系统的服务要通过更多的软件层次来实现。宿主虚拟机方式被使用在VMware的实现中,它是一个现代的系统虚拟机,运行于IA-32硬件平台之上。
图9 虚拟机位于操作系统之上
对于传统的系统虚拟机,所有的系统软件和应用软件都使用与底层硬件相同的指令集架构。但在另外的一些情况下,客户系统和是宿主系统使用不同的指令集架构。例如,基于PowerPC的Apple系统和Windows系统使用不同的指令集架构,这也是两种最流行的桌面系统。因为软件系统和硬件系统紧密地联系在一起,会导致用户需要购买同一软件的针对不同 平台的版本,这会使软件支持变得复杂并且会是用户的购买力下降。这种情况导致了对系统虚拟机的需求,这是一个完整的软件系统,包括操作系统和应用程序,它能够支持在宿主系统上运行另一种不同的指令集和操作系统。因为指令集结构不同,应用程序和操作系统代码都需要仿真。对完整系统虚拟机来说,常用的一种实现 方法是将虚拟机管理器和客户软件放在一个传统的宿主操作系统上。
图9描述了一个系统虚拟机,它建立在一个拥有自己的操作系统和应用程序的传统系统之上。这种虚拟机的一个例子是Virtual PC,它能使Windows操作系统运行在Macintosh系统上。虚拟机软件是作为一个应用程序运行在宿主操作系统上,它使用宿主系统的指令集操作。为了实现这种类型的虚拟机,虚拟软件需要仿真整个硬件环境。它必须控制所有指令的仿真,并且需要将客户系统的指令集操作转换成对应的宿主操作系统的系统调用。即使使用二进制翻译,也无法有效利用宿主系统指令集架构的特性,如虚拟内存管理和中断管理。另外,如果客户系统和宿主系统的硬件资源有显著的差异,则会带来实现上的一些问题, 而解决这些问题是实现完整系统虚拟机面临的主要挑战。
以上所讨论的虚拟机模型, 都是为了功能和移植,包括在同一个平台上支持多种操作系统或者多种指令集架构。事实上, 这些虚拟机都是基于已经存在的某种标准指令集或操作系统开发的。大体上来说,提高性能不是一个目标, 反而降低性能损失常常是一个需要考虑的问题。
例子:
这里介绍一些本人最近正在研究的lguest半虚拟化硬件虚拟机:
图10 lguest虚拟机架构
Lguest 的核心模块是lg 可加载模块。刚开始,一个小的系统管理程序(hypervisor)被加载到内核空间。该系统管理程序运行在lguest虚拟空间的ring1上,利用Intel处理器提供的段保护机制保证不会受到lguest的guest程序的修改。主要功能是实现内核和虚拟客户之间的切换。Lg模块同时也虚拟了基本的I/O子系统(DMA机制)。
用户模式的系统管理程序客户端(user-mode hypervisor client)主要工作是申请一片内存充当客户操作系统的物理地址,并将自己设置在该物理内存的高端地址,空出的空间留给客户操作系统。详情请看:http://blog.csdn.net/micklongen/category/474394.aspx。
小结
虚拟机现在应用范围越来越广泛,不同的技术和实现方式也层出不穷,以至于难以给虚拟机一个准确地定义和飞类。与虚拟机技术 紧密相连的是虚拟化技术,这是一个内容更丰富范围更广的概念,简单来说,虚拟机虚拟的目标基本上都是一个较完整的计算机,而虚拟化虚拟的目标则广泛得多, 可以是完整的计算机,也可以是部分资源。参考文献
[1] 虚拟机的设计与实现:C/C++,(美)Bill Blunden,2003
[2] 虚拟机—系统与进程的通用平台,James E.Smith/Ravi Nair ,2006
[3] An introduction to lguest,corbet ,2007