公司如何建立网站,如何开发高端市场,怎么做网站的移动端适配版,资金盘网站建设虚拟机
在计算领域#xff0c;VM#xff08;虚拟机#xff09;是一个术语#xff0c;指的是模拟/虚拟化计算机系统/架构的系统。
一般来说#xff0c;虚拟机有两类#xff1a;
系统虚拟机提供真实机器的完整替代品。 它们实现了足够的功能#xff0c;允许操作系统在它…虚拟机
在计算领域VM虚拟机是一个术语指的是模拟/虚拟化计算机系统/架构的系统。
一般来说虚拟机有两类
系统虚拟机提供真实机器的完整替代品。 它们实现了足够的功能允许操作系统在它们上运行。 它们可以共享和管理硬件有时多个环境可以在同一台物理机器上运行而不会互相妨碍。处理虚拟机更简单旨在在与平台无关的环境中执行计算机程序。 JVM 是进程虚拟机的一个很好的例子。
在本文中我们将开发一个简单的进程虚拟机旨在在独立于平台的环境中执行简单的计算机程序。 我们的虚拟机基于 LC-3 计算机架构并且能够解释和执行 LC3 汇编代码的子集。 Little Computer 3或 LC-3是一种计算机教育编程语言一种汇编语言一种低级编程语言。 它具有相对简单的指令集但可用于编写中等复杂的汇编程序并且是 C 编译器的可行目标。 该语言比 x86 汇编语言简单但具有许多与更复杂语言类似的功能。 这些功能使其值得入门教学因此它最常用于向计算机科学和计算机工程学生教授编程和计算机体系结构基础知识 为简单起见我们特意从以下功能中剥离了 LC-3 实现中断处理、优先级、进程、状态寄存器 (PSR)、特权模式、管理程序堆栈、用户堆栈。 我们将只虚拟化最基本的硬件并且我们将通过陷阱与外界stdin、stdout进行交互。
我们受 LC-3 启发的 VM 与当今大多数通用计算机一样基于冯·诺依曼计算机模型它将具有三个主要组件CPU、主存储器、输入/输出设备。
实现
我们的虚拟机功能如下
我们将程序加载到主存中在RPC寄存器中我们保存当前需要执行的指令我们从指令中获取操作码前 4 位并据此解码其余参数我们执行与给定指令相关的方法我们增加 RPC 并继续下一条指令
内存
我们的机器有 WUINT16_MAX 个字每个字 N16 位。从 C 的角度来看我们的内存可以定义为
uint16_t PC_START 0x3000;
uint16_t mem[UINT16_MAX1] {0};寄存器
我们的 VM 共有 10 个寄存器每个寄存器 16 位
从代码的角度来看我们可以按如下方式实现它们
enum regist { R0 0, R1, R2, R3, R4, R5, R6, R7, RPC, RCND, RCNT };
uint16_t reg[RCNT] {0};指令
指令就像我们向虚拟机发出的命令。
为了提取操作码本身我们可以编写一个实用宏来应用简单的按位技巧
#define OPC(i) ((i)12)我们可以在 C 中执行的一个好技巧从数据建模的角度来看是将所有可能的指令及其关联的 C 函数保存在数组中。 索引将代表实际的操作码毕竟操作码是从 0 到 15 的数字并且该值将是指向相应 C 函数的指针。
#define NOPS (16) // number of instructions
typedef void (*op_ex_f)(uint16_t instruction);
//
// ... other operations here
//
static inline void add(uint16_t i) { /* code here */ }
static inline void and(uint16_t i) { /* code here */ }
//
// ... other operations here
//
op_ex_f op_ex[NOPS] { br, add, ld, st, jsr, and, ldr, str, rti, not, ldi, sti, jmp, res, lea, trap
};加法
逻辑位“与”
ld - 加载 RPC 偏移量
ldi - 间接加载
ldr - 加载偏移量
lea - 加载有效地址
not - 按位求补
st - 存储
sti - 间接存储
str - 存储偏移量
jump - 跳转
…
加载和执行程序
我们只缺少两个功能主循环和加载程序的能力。
我们虚拟机的主循环如下所示
bool runningtrue;
uint16_t PC_START 0x3000;
void start(uint16_t offset) { reg[RPC] PC_START offset; // The RPC is setwhile(running) {uint16_t i mr(reg[RPC]); // We extract instructions from the memory// location pointed by RPC // We (auto)increment RPC op_ex[OPC(i)](i); // We execute each instruction}
}现在唯一缺少的是将程序加载到我们的虚拟机中的能力在这方面我们将编写一个 ld_img 方法能够将二进制文件直接加载到我们的主内存中
void ld_img(char *fname, uint16_t offset) {// Open (binary) file containing the VM programFILE *in fopen(fname, rb); if (NULLin) {fprintf(stderr, Cannot open file %s.\n, fname);exit(1); }// The position from were we start copying the file// to the main memoryuint16_t *p mem PC_START offset;// Load the program in memoryfread(p, sizeof(uint16_t), (UINT16_MAX-PC_START), in);// Close the file streamfclose(in);
}该方法返回 void 并接受两个输入参数
包含我们程序的二进制文件的路径我们开始将第一条程序指令加载到主内存中的偏移量
我们虚拟机的主要方法如下所示
int main(int argc, char **argv) {ld_img(argv[1], 0x0);start(0x0);return 0;
}我们的第一个程序将从键盘读取两个数字并将它们的总和打印到标准输出。
0xF026 // 1111 0000 0010 0110 TRAP tinu16 ;read an uint16_t in R0
0x1220 // 0001 0010 0010 0000 ADD R1,R0,x0 ;add contents of R0 to R1
0xF026 // 1111 0000 0010 0110 TRAP tinu16 ;read an uint16_t in R0
0x1240 // 0001 0010 0010 0000 ADD R1,R1,R0 ;add contents of R0 to R1
0x1060 // 0001 0000 0110 0000 ADD R0,R1,x0 ;add contents of R1 to R0
0xF027 // 1111 0000 0010 0111 TRAP toutu16 ;show the contents of R0 to stdout
0xF025 // 1111 0000 0010 0101 HALT ;halt语法对用户不友好不是吗我们的程序其实就是这一系列数字0xF026 0x1220 0xF026 0x1240 0x1060 0xF027 0xF025。但如果我们仔细观察就会发现在这些数字中我们一直在编码汇编指令。
例如让我们看一下这个数字0xF026。其二进制表示为1111 0000 0010 0110。很容易看出1111是trap的编码TRAPVECT是100111对应tinu16。
或者为了更直观的表示我们来分析 0x1220
0x1220 -0001 001 000 1 00000
ADD R1 R0 IMM50 运行我们的第一个程序
#include stdio.h
#include stdlib.huint16_t program[] {/*mem[0x3000]*/ 0xF026, // 1111 0000 0010 0110 TRAP trp_in_u16 ;read an uint16_t from stdin and put it in R0/*mem[0x3002]*/ 0x1220, // 0001 0010 0010 0000 ADD R1,R0,x0 ;add contents of R0 to R1/*mem[0x3003]*/ 0xF026, // 1111 0000 0010 0110 TRAP trp_in_u16 ;read an uint16_t from stdin and put it in R0/*mem[0x3004]*/ 0x1240, // 0001 0010 0010 0000 ADD R1,R1,R0 ;add contents of R0 to R1/*mem[0x3006]*/ 0x1060, // 0001 0000 0110 0000 ADD R0,R1,x0 ;add contents of R1 to R0/*mem[0x3007]*/ 0xF027, // 1111 0000 0010 0111 TRAP trp_out_u16;show the contents of R0 to stdout/*mem[0x3006]*/ 0xF025, // 1111 0000 0010 0101 HALT ;halt
};int main(int argc, char** argv) {char *outf sum.obj;FILE *f fopen(outf, wb);if (NULLf) {fprintf(stderr, Cannot write to file %s\n, outf);}size_t writ fwrite(program, sizeof(uint16_t), sizeof(program), f);fprintf(stdout, Written size_t%lu to file %s\n, writ, outf);fclose(f);return 0;
}源代码
参阅一 - 亚图跨际
参阅二 - 亚图跨际