百科问答小站 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

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




  

相关话题

  计算机是如何启动的? 
  为什么 Java 和 JS 等语言需要 VM,不能直接操作内存堆栈空间? 
  如果计算机没有浮点运算能力,系统能正常启动吗? 
  人工智能为什么越来越看不懂了? 
  使用 open addressing 的 Hash 表载荷过高为什么会降低 CPU 的缓存命中率? 
  分析、抽象代数这种课对搞 data science 帮助大吗? 
  有没有一段代码,让你为人类的智慧击节叫好? 
  Linus 真心诚意地夸过哪些人或技术? 
  按下开机键后,电脑都干了些什么? 
  AI 20年内将会给我们的生活带来什么改变? 

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





© 2024-12-22 - tinynew.org. All Rights Reserved.
© 2024-12-22 - tinynew.org. 保留所有权利