虚拟机
由真实物理设备构造的计算机,用术语来说就是一台真实计算机或硬件计算机.从程序设计的观点来看,它是由机器定义的设备指令集.一个操作系统构建在机器的顶层,用于管理访问机器和提供一些附加服务.这些由操作系统提供的服务构成了另一台机器,即虚拟机.
一种程序设计语言提供了一组运算集合.这样,例如,它可能会被称作一台Pascal计算机或是一台Scheme计算机.对于程序员来说,程序设计语言是一台计算机;程序设计语言定义了一台虚拟机.Simple的虚拟机包含一个把变量和值关联起来的数据区域,和用于操作这个数据区域的程序.
在程序员认为的程序和由操作系统提供的虚拟机之前还有另一种虚拟机.它包含了必要的数据结构和算法去支持程序的执行.这样的虚拟机是一种语言的实时系统.它的复杂性规模大小从没有一点实质,如FORTRAN语言,延伸到为了支持极其高级系统内存管理和内部进程通信的并发程序设计语言,如SR.作为Simple的实时系统包含执行代码的处理单元能力和一个数据区域,在这个数据区域中,赋给变量的值通过数据区域的偏址来获得.
用户程序构成另一个虚拟机类.
一个堆栈机
堆栈机(S-machine)1是一个为了简化块结构语言实现而组织的一个堆栈机器.通过一个栈的活化记录,它可提供动态存储分配.这个活化记录被链接,以提供静态作用域支持和包含支持过程的上下文信息
机器的组织:堆栈机包含两个存储器,一个程序存储器--C(组织成一个只读的数组).和一个数据存储器--S(组织成一个栈).这里有四个寄存器,一个指令寄存器IR(包含正在被解释的指令),一个栈顶寄存器T(包含栈顶元素的地址),一个程序地址寄存器PC(包含下一个将要取指解释),和一个当前活化记录寄存器AR(包含正在被解释的过程活化记录基地址).每一个C中的存储单元可以存储一条指令.而每个S存储单元可以存储一个地址或一个整数.每个指令包含三个域,一个操作码和两个操作数.
指令集:S-code是堆栈机的机器语言.每条S-code占四个字节.第一个字节是操作码.这里有9条基本S-code指令,每条指令有为不同的操作码.S-code指令的第二个字节包含0或一个常量,或一个条件转移指令的条件码.最后两个字节可作为一个十六位的整型操作数,例如代表一个常量值,或是一个变量在栈中的偏移地址,或一条S-code指令地址,或是一个操作数,或是一个特定的函数号,这取决于指令的操作码.
每条指令的操作用英语和数学形式化的混合来描述.数学形式化用于标记发生在寄存器和堆栈机的栈中的值的变化.数据访问和存储指令要求一个在活化记录中的偏移地址,和一个不同于引用层和定义层的层.过程调用要求一个代码地址和一个不同于引用层和定义层的层.
------------------------------------------------------------------------------------------------------------------
1:这是改编自:Nikaus Wirth,Algorithms + Data Structures =Programs Prentice-Hall,Englewood Cliffs,N.J.,1976
Instruction(指令) Operands(操作数) Comments(注释)
READ 0,N 输入整数到N:S(N):=INPUT
WRITE 输出栈顶元素:Output :=S(T);T:=T-1
OPR 算术与逻辑操作
0,0 过程和函数,返回操作T:=AR-1;AR:=S(T+2);P:=S(T+3)
ADD ADD:S(T-1):=S(T-1)+S(T);T:=T-1
SUB SUBTRACT:S(T-1):=S(T-1)-S(T);T:=T-1
MULT MULTIPLY:S(T-1):=S(T-1)*S(T);T:=T-1
DIV DIVIDE:S(T-1):=S(T-1)/S(T);T:=T-1
MOD MOD:S(T-1):=S(T-1) mod S(T);T:=T-1
EQ 测试是否相等:S(T-1):=if S(T-1)=S(T) then 1 else 0;T:=T-1
LT 测试是否是于:S(T-1):=if S(T-1)<S(T) then 1 else 0;T:=T-1
GT 测试是否大于:S(T-1):=if S(T-1)>S(T) then 1 else 0;T:=T-1
LD_INT 0,N 把一个常量值装入栈中:T:=T+1;S(T):=N
LD_VAR L,N 把一个在层中偏移L,在栈中基地址为N`的变量值送入 栈,T:=T+1;S(T):=S(f(L,AR)+N)+3
STORE L,N 把栈中的值存储在层偏移为L,栈基地址为N 的变量中S(f(ld,AR)+os+3):=S(T);T:=T-1
CAL L,N 调用S-code地址在N中,声明在层编移为L的 过程或函数
CAL 255,0 调用在内存中的过程地址:POP 地址,PUSH返回地 址.JUMP 地址
DATA 0,NN 栈指针加上NN: T:=T+NN
GOTO 0,N JUMP: P:=N
JMP_FALSE C,N JUMP:if S(T) = C then P:=N;T:=T-1
在不同于当前过程和调用过程的静态层,被称作ld.os是在活化记录中的偏移,ld是不同于当前的活化记录的静态层,而其值在活化记录中保存,
f(ld,a) = if i=0 then a else f(i-1,S(a))
运算:栈机的寄存器和栈按如下初始化:
P=0. {程序计数器}
AR=0; {活化记录}
T=2; {栈顶}
s[0] :=0; {静态链}
s[1] :=0; {静态动态链}
s[2] :=0; {返回地址}
机器重复地取出以寄存器P为地址,并以寄存器P为增量的指令,并一直执行指令直到寄存器P包含一个0
execution-loop : I:= C(P);
P := P+1;
imterpret(I);
if { P<=0 -> halt
& p>0 ->execution - loop }
堆栈机的模块
实现堆栈机是很直接的.
指令集和一条指令的结构被定义如下:
/*OPERATIONS : Internal Representation */
enum code_ops { HALT , STORE , JMP_FALSE , GOTO,
DATA, LD_INT,LD_VAR,
READ_INT, WRITE_INT ,
LT, EQ, GT, ADD, SUB, MULT,DIV, PWR };
/*OPERATIONS: External Representation */
char *op_name[] = { "halt", "store", "jmp_false", "goto",
"data", "ld_int", "ld_var",
"in_int", "out_int",
"lt", "eq", "gt", "add", "sub", "mult", "div", "pwr" };
struct instruction
{
enum code_ops op;
int arg;
};
内存被分为两个段,一个代码段和一个运行时数据和表达式栈:
struct instruction code[999];
int stack[999];
寄存器组的定义,如程序计数器pc,指令计数器ir,活化记录ar(指向当前活化记录的最开始处),和指向栈顶的指针是很直观的:
int pc =0;
struct instructions ir;
int ar =0;
int top=0;
取指-执行周期重复地运行直到遇到一条halt指令.
void fetch_execute_cycle()
{ do { /* Fetch */
ir = code[pc++];
/*Execute */
switch (ir.op) {
case HALT : printf( "halt/n" ); break;
case READ_INT : printf( "Input: ");
scanf( "%1d", &stack[ar+ir.arg] ); break;
case WRIRE_INT: printf( "Output : %d/n",stack[top--] ); break;
case STORE : stack[ir.arg] = stack[top--]; break;
case JMP_FALSE: if ( stack[top--] == 0 )
pc = ir.arg;
break;
case GOTO : pc = ir.arg; break;
case DATA : top = top + ir.arg; break;
case LD_INT : stack[++top] = ir.arg; break;
case LD_VAR : stack[++top] = stack[ar+ir.arg]; break;
case LT : if ( stack[top-1] < stack[top] );
stack[--top] = 1;
else stack[--top] = 0;
case ADD : stack[top-1] = stack [top-1] + stack[top];
top--;
break;
case SUB : stack[top-1] = stack [top-1] - stack[top];
top--;
break;
case MULT : stack[top-1] = stack [top-1] * stack[top];
top--;
break;
case DIV : stack[top-1] = stack [top-1] / stack[top];
top--;
break;
/*译注:这里有问题和MULT一样*/
case PWR : stack[top-1] = stack [top-1] * stack[top];
top--;
break;0
default : printf( "%sInteral Error : Memory Dump/n");
break;
}
}
while (ir.op != HALT);
}