《第八章虚拟机》word版.doc_第1页
《第八章虚拟机》word版.doc_第2页
《第八章虚拟机》word版.doc_第3页
《第八章虚拟机》word版.doc_第4页
《第八章虚拟机》word版.doc_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第八章 虚拟机虚拟机技术并不是一项全新的技术。现在我们经常可以看到很多的虚拟机。首先,比如说我们很熟悉的JAVA虚拟机、还有GWBasic解释器、Microsoft Word的WordBasic宏解释器等等。虚拟机的应用场合很多,它的主要作用是能够运行一定规则的描述语言。 虚拟机的“虚拟”二字,有着两方面的含义:其一是指运行一定规则的描述语言的机器并不一定是一台真实地以该语言为机器代码的计算机,比如JAVA想做到跨平台兼容,那么每一种支持JAVA运行的计算机都要运行一个解释环境,这就是JAVA虚拟机;另一个含义是指运行对应规则描述语言的机器并不是该描述语言的原设计机器,这种情况也称为仿真环境。比如Windows的MS-DOS Prompt就是工作在V86 方式的一个虚拟机,虽然在V86方式,实x86指令的执行和在实地址方式非常相似,但是Windows为MS-DOS程序提供了仿真的 ( 相对于物理1M以下内存是虚假的)内存空间。 跨越计算机平台的虚拟机也有很多,比较典型的是在很多Unix下运行MS-DOS或Windows程序的仿真器。在一台非WinTel计算机上运行 MS-DOS应用程序,首先需要对MS-DOS应用程序所使用的x86指令进行解释执行,并要提供完整的仿真MS-DOS中断、功能调用和绝大多数BIOS调用,并要解决MS-DOS 环境所使用的内存特点。根据仿真的彻底程度不同,所获得的兼容性也是不同的。 如果要仿真 MS-Windows 程序的运行环境,除了上述工作以外,基本上要完整地再做一份完全兼容的 Windows API,并且会遇到DDE、OLE、DirectX 等类似的令人头疼的兼容性问题。同时,仿真运行的程序必定是以比宿主计算机慢得多的速度来运行的。 因此,我们大致可以看到一个比较完整的虚拟机需要在很多的层次上做仿真,总的来说是分为“描述仿真”和“环境仿真”两大部分。最简单的仿真环境几乎不能算是虚拟机:比如为了运行使用磁盘加密而制作的钥匙盘仿真驻留程序,它仅仅做一些诸如修改Int13这样的小手段,而不必包括指令执行的“描述仿真”;而象 Sourcer 这样的反汇编工具则是更注重指令解释, 而不必关心程序的运行结果,因此在环境仿真上所做的工作要少得多。完整、复杂的虚拟机是几乎没有尽头的,假想我们要在一台Unix计算机上运行一个看VCD的Windows 95程序,或是运行一个使用8259中断、8237DMA的程序, 有些仿真在理论上是可以实现的,有些则很可能行不通。所以,一个实用的虚拟机是根据需要和可能这两方面的因素来构造的,要权衡时间/空间复杂度、仿真兼容性、 运行性能和代价等诸多因素,根据实际情况来设计和实现。8.1虚拟机结构设计粗略的介绍完虚拟机的概念以后,接下来将介绍在我们设计的虚拟机中是如何实现对“仿真”的实现的,在我们设计的虚拟机中,它主要包含下面几个部分:一个完整的指令系统、一组寄存器、一个栈、一个用于对象存储的堆、一组符号表和一个代码存储区。8.1.1 指令系统 这里只简单介绍各条指令,详细的说明将在附录1汇编指令的使用说明中给出:MOV功能: 把源操作数送给目的操作数语法: MOV 目的操作数,源操作数PUSH功能: 把操作数压入堆栈语法: PUSH 操作数POP功能: 把操作数取出堆栈语法: POP 操作数ADD功能: 加法指令语法: ADD OP1,OP2 SUB功能:减法指令语法: SUB OP1,OP2 INC功能: 把OP的值加一或减一语法: INC OP DEC OPDEC功能: 把OP的值减一语法: DEC OPMUL功能: 乘法指令语法: MUL OP1,OP2DIV功能:除法指令语法: DIV OP1,OP2JMP功能: 无条件跳往指定地址执行语法: JMP 地址条件转移指令 JXX功能: 当特定条件成立则跳往指定地址执行语法: JXX 地址说明: JA/JNBE 不小于或不等于时转移. JAE/JNB 大于或等于转移. JB/JNAE 小于转移. JBE/JNA 小于或等于转移. JG/JNLE 大于转移. JGE/JNL 大于或等于转移. JL/JNGE 小于转移. JLE/JNG 小于或等于转移. JE/JZ 等于转移. JNE/JNZ 不等于时转移. CALL功能: 子程序调用指令语法: CALL 地址VCALL功能: 虚函数调用指令语法: VCALL 地址RET功能: 子程序返回指令语法: RET / RET nNEW功能: 对象地址分配指令语法: NEW XXXDELET功能:地址回收指令语法: DELET XXXHALT功能: 停机指令语法: HALTTHROW功能: 异常抛出指令语法: THROW XXX8.1.2寄存器设计在我们的虚拟机中,寄存器分为操作寄存器和通用寄存器两大类:1 操作寄存器操作寄存器通常用于虚拟机自身运行时的控制数据暂存:Bp:栈底指针寄存器Pc:指令地址寄存器Sp:栈顶指针寄存器Hp:堆指针寄存器2通用寄存器通用寄存器通常用于虚拟机运行时的运算数据暂存:AX,BX,CX8.1.3.寻址方式1立即寻址 立即操作数可以是一个整数,并且是指令的一部分。立即数据总是紧跟在指令操作码之后并和操作码一起存放在代码段中,因而立即数据总是和操作码一起被放入指令队列里,在指令执行时不需再存取存储器。使用立即寻址的指令主要用来给寄存器赋初值,例如: MOV AX,5执行结果将使寄存器AX中的值为5。2寄存器寻址 操作数存放在指令规定的寄存器中,在我们的虚拟机中,寄存器可以是AX,BX,CX,BP,PC,SP例如: MOV AX, BX执行结果将使寄存器AX中的值为BX的值。3直接寻址操作数的有效地址是指令的一部分,它与操作码一起存放在代码段中,但操作数一般是在数据段中,在我们的虚拟机中,只涉及了简单的处理,存储器的设计也相对的来说比较简单,因而这种寻址方式并不是像在80X86机器中一样,以DS的内容为基准,而是一栈的起始位置为基准。 例如: MOV AX, 20这则指令式将栈中物理地址为20单元的内容传送AX寄存器。4寄存器间接寻址 操作数的地址存放在寄存器中,即AX,BX,CX,BP,PC,SP例如: MOV AX, BX它表示操作数在数据段中,存放单元地址在寄存器BX中。这则指令式将栈中物理地址为BX的内容的单元的值传送AX寄存器。5寄存器相对寻址方式操作数的地址是一个整数的位移量与基址指示器BP或某个变址寄存器之和,例如: MOV AX, BP+5它是BP的内容加上5的结果作为操作数存放的单元地址。这则指令式将栈中物理地址BP的内容加上5的单元的值传送AX寄存器。8.1.4 内存设计在我们的虚拟机中,存储区主要有三个部分构成:全局变量存储区、活动记录区、对象存储区(堆)。1全局变量存储区在我们的虚拟机中,每个程序它都使用栈最底部的那几个单元来作为全局变量存储区。这样做的原因是和后面的活动记录区设计联系在一起的。因为在活动纪录区,存储空间的分配大小是与具体的函数参数相关的,并且它们是生存期也会随着函数的返回而结束。2活动记录区在活动记录区中包含的信息用于对象的动态链接,正常的方法返回以及异常传播。动态链接 运行环境包括对指向当前类和当前方法的解释器符号表的指针,用于支持方法代码的动态链接。方法的class文件代码在引用要调用的方法和要访问的变量时使用符号。动态链接把符号形式的方法调用翻译成实际方法调用,装载必要的类以解释还没有定义的符号,并把变量访问翻译成与这些变量运行时的存储结构相应的偏移地址。动态链接方法和变量使得方法中使用的其它类的变化不会影响到本程序的代码。正常的方法返回如果当前方法正常地结束了,在执行了一条具有正确类型的返回指令时,调用的方法会得到一个返回值。执行环境在正常返回的情况下用于恢复调用者的寄存器,并把调用者的程序计数器增加一个恰当的数值,以跳过已执行过的方法调用指令,然后在调用者的执行环境中继续执行下去。3对象存储区(堆)目前的语言中尚不需要在堆中分配空间;因此在这里不做介绍。4栈中内容介绍栈中内容如右图8.1所示:从栈的最底部开始,我们在那里存储了程序中的全局变量,将需要对全局变量进行访问时,我们首先通过查询符号表,获取它在栈中相对栈底的偏移位置,然后直接读取这个单元的内容即可。Local varsparamsOld_pcBpPcParams全局数据区Old_bplocal vars分配了全局变量之后,就是程序的活动记录区,每个函数的活动记录格式如下:Bp:栈底指针Pc:返回地址Local vars:局部变量Params:形式参数 图8.1 栈中的内容8.2虚拟机运行过程在前一节中,我们介绍了虚拟机的结构,在接下来的内容中,我们将详细地讲述虚拟机怎样执行我们生成的代码。8.2.1程序的装入第一步:对源文件进行语法分析、词法分析直至生成目标代码。我们在这里使用一个文件名位t.cm 的测试源文件,它的内容为:void main() int a;int b;a=4;b=5+a;最后我们将得到一组文件:t_syntaxTree.xml,t_SymbolTable.xml,t_asm.xml,t.err, t.xml,asm.txt。第二步:运行虚拟机程序,我们将见到以下的界面:图8.2 界面第三步:点击“装入元数据”按键,将符号表数据导入,即文件t_SymbolTable.xml。第四步:点击“装入中间代码”按键,将指令数据导入,即文件t_asm.xml。到这里,程序的装入工作就已经完成了,然后我们点击“单步执行”按键,就可以运行代码了。8.2.2程序的执行首先,我们来看看生成的代码:line0:CALL NEAR PTR _MAINline1:HALT line2:main:MAIN PROC NEARline3:ADD SP 6line4:MOV BP+2 0line5:MOV BP+3 0line6:MOV AX -1line7:MOV BP+4 AXline8:MOV BP+5 SPline9:MOV AX 4line10:MOV BP+6+0+0 AXline11:MOV AX BP+6+0+0line12:MOV BX AXline13:MOV AX 5line14:ADD AX BXline15:MOV BP+6+1+0 AXline16:RET line17:MAIN ENDP 在虚拟机的初始阶段,寄存器PC是指在指令的第一行的,即line0,也就是说,虚拟机是从第一行代码开始执行的。STEP 1:调用 _MAIN 函数:当前的函数栈基址BP、函数返回地址PC进栈,通过查表得到 _MAIN 函数的地址,line2,PC转至line2;STEP 2STEP 8:_MAIN 函数活动纪录的初始化;STEP 3:根据符号表中_MAIN 函数的信息,设置栈顶指针;STEP 4:初始_MAIN 函数信息;STEP 5:初始_MAIN 函数回退信息;STEP 6STEP 7:因为是 _MAIN 函数,置BP+4位置值为-1;STEP 8:保存当前栈顶指针;STEP 9STEP 10:借助AX寄存器,初始变量a的值;STEP 11:准备进行加法操作,先将变量a的值从栈中取到寄存器AX中;STEP 12:然后将a的值寄存器AX中赋给BX;STEP 13:将另一个操作数“5”,传给寄存器AX;STEP 14:进行加法操作,结果保存在AX中;STEP 15:将保存在AX中的结果传回b在栈上的单元STEP 16:_MAIN 函数执行完,清除活动纪录,退栈,返回返回地址,PC转至line1STEP 17:停机指令,结束执行。8.3核心代码的实现在这一节中,我们将更加深入的介绍虚拟机的执行过程,也就是,单个指令如何在虚拟机中实现。8.3.1算术运算指令:AddAdd指令是虚拟机中的加法运算指令,它在代码中的格式如下:Add Des, Source 其中,Des为目的操作数,Source为源操作数;Des的寻址范围为Ax,Bx,Sp寄存器,Source的寻址范围为Ax,Bx,Sp寄存器和存储器空间。这条指令的运行结果为,将目的操作数与源操作数相加,并将结果放回目的操作数中保存。所以,在虚拟机中,我们可以按照如下方式来实现Add指令: Function Add(des,source)Val ArgValue(source);/根据寻址方式获取源操作数Case des ofAX: cpu.ax val+ cpu.ax; break;BX:cpu.bx val+cpu.bx;break;SP:cpu.sp val+cpu.sp;break;default:break;8.3.2跳转指令:JmpJmp指令是虚拟机中的加法运算指令,它在代码中的格式如下:Jmp Des, 其中,Des为该指令唯一操作数;Des的寻址范围为Ax,Bx,Sp寄存器和存储器空间。这条指令的运行结果为,虚拟机跳转到操作数表明的地址处继续执行。Function Jmp (des)Addr ArgValue(des);/根据寻址方式获取操作数cpu.pc Addr; /将sp寄存器的值置为跳转地址所以,在虚拟机中,我们可以按照如下方式来实现Jmp指令:8.3.3条件跳转指令:JeJe指令是虚拟机中的加法运算指令,它在代码中的格式如下:Je Des, 其中,Des为该指令唯一操作数;Des的寻址范围为Ax,Bx,Sp寄存器和存储器空间。这条指令的运行结果为,虚拟机跳转到操作数表明的地址处继续执行。Function Je (des) If (cpu.zf = 0) /判断标志寄存器Addr ArgValue(des);/根据寻址方式获取操作数cpu.pc Addr; /将pc寄存器的值置为跳转地址elsecpu.pc+;所以,在虚拟机中,我们可以按照如下方式来实现Je指令:8.3.4栈指令:PushPush指令是虚拟机中的加法运算指令,它在代码中的格式如下:Push Source, 其中,Source为该指令唯一操作数;Source的寻址范围为Ax,Bx,Bp,Sp,This寄存器和立即数。这条指令的运行结果为,将Source处的内容放入栈中。Function Push (source)Case des ofAX: Val cpu.ax; break;BX:Val cpu.bx;break;BP:Val cpu.bp;break;SP: Val cpu.sp; break;THIS:Val cpu.this;break;default:Val int(Source);break;this.stackcpu.sp Val;cpu.sp+;所以,在虚拟机中,我们可以按照如下方式来实现Push指令:8.3.5 函数调用指令:C

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论