程序员程序员技术栈

虚拟机VM ( c++ 版本)1

2019-05-12  本文已影响4人  zidea

今天通过学习来尝试自己去写一个虚拟机,我们一切先从简单开始。做事总是需要找一个原因,也就是为什么做这个件事,做这件事的意义,发明蒸汽机为了人类从繁重的劳动中解放出来更多思考,发明计算机为了辅助人类从重复计算和繁重计算中解放出来去享受生活吗? 结果带来负效应 996。

virtual-machind.jpg

CLR常用简写词语,CLR是公共语言运行库(Common Language Runtime)和Java 虚拟机一样也是一个运行时环境,它负责资源管理(内存管理和垃圾收集等),并保证应用和底层操作系统之间必要的分离。CLR存在两种不同的翻译名称:公共语言运行库和公共语言运行时。

VM 的基本结构

stack vm 结构图

虚拟机实际上对物理计算机进行抽象和虚拟化,所以要写一个虚拟机,首先需要对计算机内部简单结构进行了解。我们开发应该是理论先行。

字节码相比于机器码是更高级以及更有有规律可寻
字节码作为工作的最小单元

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("hello world");
    }
}

定义字节码

vm_instruction.JPG

表中列出应该是助记符应该理解为字节码的别名,字节码应该是以字节为单位的 16进制的数值,为了便于阅读这些字节码将字节码对应别名。

vm_function.JPG

这张图要说起来还是比较复杂的,应该说相当复杂了,大家需要理解一下什么是栈帧。动画也好电影也好都是由一帧一帧图所组成的。这样按一定循序不断切换的图片最终形成了我们看到的精彩的电影。

栈帧也是我们程序的组成部分,很少有像我这样了解 CG 的程序员吧,所以我们的程序也是🈶一个一个画面来组成的,程序运行也是一个个按一定顺序,顺序不是固定的,根据用户的交互而定。所以栈帧里存放是一个一个的代码块,栈帧入栈时候会记录下调用这个栈帧的栈帧的信息,也就是当这个栈帧结束后他会返回到上一个栈帧。就是这样连续的或者说一层套一层的栈帧组成我们应用。

能够写出自己语言和虚拟机不是每一个程序员的梦想吗?
朝着这个梦想不断努力,哪怕自己仅仅迈出一小步都会感到欣慰,因为今天的一小步就是明天的一大步。


virtual-machind.jpg
无论指针、数据都是 int 类型这里统一使用 int32 进行保存。
#ifndef STACK_VM_H
#define STACK_VM_H

#include <iostream>
#include <stdint.h>
#include <vector>

// type definitions
typedef int32_t i32;

//隐式作为 32位i 进行处理

class StackVM
{
    i32 pc = 100;            //program counter
    i32 sp = 0;              //stack pointer 栈的指针
    std::vector<i32> memory; //虚拟内存
    i32 typ = 0;             //类型
    i32 dat = 0;             //数据
    i32 running = 1;

    //private function 获取数据类型和值
    i32 getType(i32 instruction);
    i32 getData(i32 instruction);
    //cup 内部不停循环地运行写函数
    void fetch();
    void decode();
    void execute();
    void doPrimitive();

  public:
    //pubic functions
    StackVM();
    void run(); //运行虚拟机
    //加载程序分配内存
    void loadProgram(std::vector<i32> prog);
};

#endif

我们先写头文件,其实无论指针还是我们内存都设计成为 32 位 int 类型所有

typedef int32_t i32;
#include "stack-vm.h"

/**
 * 指令格式
 * header 2 字节
 * data 30 字节
 * 
 * header format
 * 0 => positive integer
 * 1 => primitive instruction
 * 2 => negative integer
 * 3 => undefined
 * 
 * */

//functions
StackVM::StackVM()
{
    //
    memory.reserve(1000000);
}
i32 StackVM::getType(i32 instruction)
{
    i32 type = 0xc0000000;
    type = (type & instruction) >> 30;
    return type;
}

i32 StackVM::getData(i32 instruction)
{
    i32 data = 0x3fffffff;
    data = data & instruction;
    return data;
}

void StackVM::fetch()
{
    pc++;
}

void StackVM::decode()
{
    typ = getType(memory[pc]);
    dat = getData(memory[pc]);
}

void StackVM::execute()
{
    if (typ == 0 || typ == 2)
    {
        sp++;
        memory[sp] = dat;
    }
    else
    {
        doPrimitive();
    }
}
void StackVM::doPrimitive()
{
    switch (dat)
    {
    case 0: //halt
        std::cout << "halt" << std::endl;
        running = 0;
        break;
    case 1: //add
        std::cout << "add " << memory[sp - 1] << " " << memory[sp] << std::endl;
        memory[sp - 1] = memory[sp - 1] + memory[sp];
        sp--;
        break;
    }
}

void StackVM::run()
{
    pc -= 1;
    while (running == 1)
    {
        fetch();
        decode();
        execute();
        std::cout << "vm: " << memory[sp] << std::endl;
    }
}

void StackVM::loadProgram(std::vector<i32> prog)
{
    for (i32 i = 0; i < prog.size(); i++)
    {
        memory[pc + i] = prog[i];
    }
}
0100_0000_0000_0000_0000_0000_0000_0001 // 16进制 0xc0000000
&
0100_0000_0000_0000_0000_0000_0000_0000 // 表示命令 0x40000001
进行**与**位运算
0100_0000_0000_0000_0000_0000_0000_0000
>> 30 (右移 30 位运算)
0000_0000_0000_0000_0000_0000_0000_0001

得到 1 所以为指令类型

1100_0000_0000_0000_0000_0000_0000_0000
0000_0000_0000_0000_0000_0000_0000_0011
&
0000_0000_0000_0000_0000_0000_0000_0011
0000_0000_0000_0000_0000_0000_0000_0000
>> 30
0000_0000_0000_0000_0000_0000_0000_000

得到类型为 0 所以为正整型

0011_1111_1111_1111_1111_1111_1111_1111
0000_0000_0000_0000_0000_0000_0000_0011
&
0000_0000_0000_0000_0000_0000_0000_0011

对于指令 0x40000001

0011_1111_1111_1111_1111_1111_1111_1111
0100_0000_0000_0000_0000_0000_0000_0001
&
0000_0000_0000_0000_0000_0000_0000_0001


#include "stack-vm.h"

int main()
{
    StackVM vm;
    std::vector<i32> prog{
        3, 4, 0x40000001, 0x40000000};

    vm.loadProgram(prog);
    vm.run();
    return 0;
}
make cpp
CFLAGS= -std=c++11

all: stack-vm

stack-vm: stack-vm.o main.o
    $(CXX) $(CFLAGS) stack-vm.o main.o -o stack-vm

main.o: main.cpp
    $(CXX) $(CFLAGS) -c main.cpp

stack-vm.o: stack-vm.h stack-vm.cpp
    $(CXX) $(CFLAGS) -c stack-vm.cpp
clean:
    rm -f *.o stack-vm
上一篇下一篇

猜你喜欢

热点阅读