百科问答小站 logo
百科问答小站 font logo



如何实现一个简单的虚拟机? 第1页

  

user avatar   doinglu 网友的相关建议: 
      

可以参考一下CPU的相关实现,做一个指令的子集就行。

如果只是做赋值、循环、函数调用的话,我们还需要引入加减法,判断,跳转功能。

然后我们管理两段空间,分别是指令空间、数据空间就好了。

指令采用一个简单方式,1个字节的指令编码,1个标志判断,2个2字节参数。

       typedef struct inst {     unsigned char code; // 指令     unsigned char cond, // 执行该指令的条件     unsigned short p1, p2; // 参数1、2 } inst_t; typedef unsigned short data_t; // 我们操作的就是16位数     

接下来我们需要有一个保存状态的结构

       typedef struct vm_state {     int ip; // 指令ptr     int flag; // 记录最后判断的标志     inst_t *code; // 代码段地址     data_t *data; // 数据段地址 } vm_state_t;     

定义一下需要的指令:

       #define IADD    1 // 加法 #define ISUB    2 // 减法 #define ICMP    3 // 判断 #define IJMP    4 // 跳转 #define IMOV    5 // 赋值 #define ISTIP   6 // 保存IP #define ILDIP   7 // 设置IP(跳转) #define ILD     8 // 加载一个立即数 #define IOUT    9 // 输出 #define ISTOP   255 // 挂起虚拟机     

定义一下执行指令的条件,也就是说在什么条件下才执行该指令

       #define FNA     0 // 任何状态下都执行 #define FEQ     1 // 状态为“相等”时执行 #define FNE     2 // 状态为“不等”时执行     

好了,然后我们可以写一个虚拟机的执行的函数了

       void execute(vm_state_t *state) {     for (;;) // 执行到挂起为止     {         inst_t *current = state->ip;         state->ip++; // 取出指令以后自动ip后移         if (current->cond != FNA && current->cond != state->flag)             // 该指令要求的状态不符合当前状态,略过             continue;         switch (current->code)         {             case IADD:                 // 将p1指向的数据加上p2指向的数据                 state->data[current->p1] += state->data[current->p2];                 break;             case ISUB:                 state->data[current->p1] -= state->data[current->p2];                 break;             case ICMP:                 // 比较p1指向的数据和p2指向的数据                 if (state->data[current->p1] == state->data[current->p2])                     state->flag = FEQ;                 else                     state->flag = FNE;                 break;             case IJMP:                 // 跳转,指令根据p1进行偏移                 state->ip += current->p1;                 break;             case IMOV:                 // 将p1指向的数据设置为p2指向的数据                 state->data[current->p1] = state->data[current->p2];                 break;             case ISTIP:                 // 把IP保存到p1指向的数据                 state->data[current->p1] = (data_t) state->ip;                 break;             case ILDIP:                 // 将IP设置为p1指向的数据,该指令会导致跳转                 state->ip = state->data[current->p1];                 break;             case ILD:                 // 将立即数p2加载到p1指向的数据                 state->data[current->p1] = p2;                 break;             case IOUT:                 // 输出p1指向的数据                 printf("%d
", state->data[current->p1]);                 break;             case ISTOP:                 return;         }     } }      

这虚拟机就算写完了。

这个虚拟机能做点什么呢?比如做一个类似这样的加法还是可以的:

       int sum = 0; for (int i = 1; i != 101; i++)     sum += i;     

我们可以这样处理:

保存sum在数据段0号

保存i在数据段1号

保存101立即数在数据段2号

保存立即数1在数据段3号

翻译的指令如下:

       0000 ILD 2, 100   // 放立即数101到2号位 0001 ILD 3, 1     // 放立即数1到3号位 0002 ILD 1, 1     // 放立即数1到变量i 0003 ILD 0, 0     // 放立即数0到变量sum 0004 ICMP 1, 2    // 比较i和101 0005 [FEQ] IJMP 3 // 如果相等(i==101)就跳转到9,因为指令执行完ip为6,所以+3就到了9  0006 IADD 0, 1    // sum += i 0007 IADD 1, 3    // i++,3号位保存的就是1 0008 IJMP -5      // 跳转到4,因为指令执行完ip为9,所以减5就到了指令4 0009 IOUT 0       // 输出sum 0010 ISTOP        // 挂起      

要想测试一下,写一个函数把这些指令放进去就行。

       #include <stdio.h> #include <stdlib.h>  inst_t sample_code[] = {     { ILD,   FNA,  2, 100 },     { ILD,   FNA,  3, 1 },     { ILD,   FNA,  1, 1 },     { ILD,   FNA,  0, 0 },     { ICMP,  FNA,  1, 2 },     { IJMP,  FEQ,  3, 0 },     { IADD,  FNA,  0, 1 },     { IADD,  FNA,  1, 3 },     { IJMP,  FNA, -5, 0 },     { IOUT,  FNA,  0, 0 },     { ISTOP, FNA,  0, 0 } }; data_t data_seg[16]; void main(int argn, char *argv[]) {     vm_state_t state;     memset(&state, 0, sizeof(state);     state->code = sample_code;     state->data = data_seg;     execute(state); }     

这个简单的虚拟机是否可以实现函数调用呢?答案是可以的,利用ISTIP、ILDIP就可以了,返回地址可以保存在数据段中。

这个虚拟机非常简单,效率低、空间浪费、功能有限,不过在这个基础上可以自己优化、扩充嘛。

关于需要阅读什么书籍?这个要看自己当前的水平了,直接介绍专业著作怕是不一定能看的下去。我建议可以自己先写着玩玩,实现一些功能,尝试编译一些自定义的脚本代码到自己的虚拟机上,然后再阅读一点编译原理、垃圾回收、JIT相关的文章,看看Intel、ARM的CPU的指令手册,把自己实验性质的虚拟机做的像样一些。玩的差不多了,可以看看

jilp.org/vol5/v5paper12

最后:上述代码回答时随手写的,没有编译调试过,难免有很多笔误...




  

相关话题

  脑机接口和成熟的基因编辑是否会改变世界? 
  如果当时 Sun 没有起诉微软,而微软继续保持对 Java 的热情的话,Java 的现状会是怎样? 
  什么是「宏」? 为什么以前计算机老师说不要用 word 中的「宏」? 
  计算机考研哪些学校性价比比较高? 
  王垠的《谈谈Parser》是在回应 winter 吗? 
  如何看待 CMU 新开设的 AI 专业? 
  如何看待 H1B 已经禁止 programmer 申请? 
  科研是大国和大经济体的游戏吗?对于小国或者小经济体有何用? 
  怎样评价光电领域本科浙大、硕博伯克利的曾博老师转行CS这件事? 
  中国数学博士的基础课明明上得比美国的多,为什么学术水平和基础还是美国好呢? 

前一个讨论
为什么Markdown不支持分栏显示?
下一个讨论
外地人的眼里,上海交大属于什么层次?





© 2025-03-25 - tinynew.org. All Rights Reserved.
© 2025-03-25 - tinynew.org. 保留所有权利