[OS] 高级语言虚拟机
1. ISA
指令集体系结构(instruction set architecture,ISA),它们是软件和硬件的分界。
20世纪40年代初,IBM 360大型计算机系列开发时,首次明确的提出了指令集体系结构的概念。
在那个项目中,IBM充分认识到了软件兼容性的重要。
IBM 360系列有很多型号,可以与很广泛的硬件资源协同工作,从而覆盖了很宽的价格和性能范围。
但是,它们拟计划能运行相同的软件。
为了成功的达到这个目标,必须准确定义和控制硬件和软件之间的接口,
而指令集体系结构实现了这个目标。
2. 进程虚拟机和系统虚拟机
2.1 系统虚拟机
从操作系统角度来看,整个系统由底层机器支撑。
一个系统是一个完整的运行环境,可以同时支撑许多可能属于不同用户的进程。
所有进程共享一个文件系统和其他的I/O资源。
进程不断的进进出出,系统环境始终存在(偶尔会重启)。
系统为进程分配物理内存和I/O资源,允许进程通过操作系统与分配给它们的资源交互,操作系统是系统的一部分。
因此,从系统的角度看,机器仅由底层硬件实现,指令集提供了系统和机器之间的接口。
2.2 进程虚拟机
从执行用户程序的进程角度看,机器由分配给进程的逻辑内存地址空间,用户级寄存器和允许执行进程代码的指令组成。
机器的I/O部分职能通过操作系统看到,进程与I/O系统交互的唯一方法是通过操作系统调用,
通常是将系统库函数作为进程的一部分来执行。
这种进程通常很短暂(虽然并不总是如此)。
它们被创建,执行一段时间,其间可能派生其他进程,最后终止。
总的来说,从进程的角度来看,机器由操作系统与底层的用户级硬件组合而成,
ABI提供了进程与机器之间的接口。
3. 高级语言虚拟机
3.1 动机
对于进程虚拟机来说,跨平台可移植性是一个非常重要的目标。
例如,FX!32系统能够把为一个流行的平台(IA-32 PC)编译的应用软件移植到一个不太流行的平台(Alpha)上。
然而,这种方法允许的跨平台兼容性仅以具体情况具体对待为基础,而且需要大量的编程工作。
例如,如果想在现有的许多硬件平台如SPARC,PowerPC及MIPS上运行IA-32二进制代码,
就需要为每一个硬件平台开发一个类似于FX!32虚拟机系统。
当主机平台运行的操作系统与编译二进制代码时运行的操作系统不一样时,这个问题就更难解决。
如果我们能退一步,在一个全面的软件框架下设计虚拟机,达到充分的跨平台可移植性将容易的多。
一种实现方法是在正在定义应用开发传经的同时设计进程虚拟机。
这个虚拟机环境不与任何实际的平台直接对应,而是与开发应用程序所用的高级语言(HLL)的特点匹配,
从而更容易实现可移植性。
这些高级语言虚拟机(HLL VMs)与前面介绍的进程虚拟机类似,
但它们把重点放在将硬件和操作系统特性对平台独立性的影响减到最小。
3.2 历史
高级语言虚拟机最早是随着Pascal编程环境开始流行的。
在传统系统中,编译器包括一个执行词法,预发和语义分析的前端,它会生成简单的中间代码——类似于机器代码,但更抽象。
例如,这种中间代码通常不包括具体的寄存器分配。
然后一个代码生成器接收中间代码,生成面向特定的ISA和操作系统的二进制机器代码。
这些二级制文件被分发,并且在支持编译时给定的ISA和操作系统的平台上执行。
如果想在其他不同平台上执行这个程序,就必须在那个平台上重新编译。
3.3 虚拟ISA
高级语言虚拟机改变了这种模式。
它的编译步骤类似于传统的模式,但在更高层次上分发程序的代码。
传统的编译器前端生成抽象的机器代码,这些代码非常类似于一种中间形式。
而在许多高级语言虚拟机中,这种中间形式是一种相当普通的基于栈的ISA,
这个虚拟ISA,实际上就是虚拟机的机器代码。
可移植的虚拟ISA代码可以被分发,在不同的平台上执行。
每一个平台都要实现能执行虚拟ISA的虚拟机。
最简单的虚拟机形式是包含一个解释器,它取指,译码,执行所需要的状态转换(例如,内存和栈的状态)。
I/O功能由一组标准的库函数完成,这个标准库是虚拟机的一部分。
在更复杂和更高性能的虚拟机中,抽象的机器代码被编译成(通过二进制翻译)可以在主机平台直接执行的主机的机器代码。
一旦在目标平台上实现了高级语言虚拟机,这个虚拟机就具有软件更容易移植的优点。
虽然虚拟机的实现需要费点力,但为每一个平台开发编译器的任务会简单很多,为每个要移植的应用重新编译也更容易。
而且,这种方法与为实际的ISA开发一个传统的仿真进程虚拟机比起来也更简单。
3.4 例子
Sun微系统公司的Java虚拟机体系结构(JVM),微软公司的公共语言基础结构(CLI),
是最近广泛使用的高级语言虚拟机的例子。
CLI是.NET框架的基础。
Java虚拟机和CLI的核心是平台独立性和高安全性。
这两个系统的ISA都是基于字节码的,即指令都被编码成一个字节序列,
每个字节是一个操作码或一个单字节的操作数,或者是多字节操作数的一部分。
这种字节码指令集是基于栈的(用于清除对寄存器的需要),有一个抽象的数据规范和存储器模型。
事实上,存储器的大小在概念上是没有限制的,假定垃圾收集器是实现的一部分。
由于基于Java或CLI的程序要求能在所有的硬件平台上执行,
因此,并不针对具体的操作系统来编译应用,而是提供一组标准作为整个执行环境的一部分。