前言:这是 Write your Own Virtual Machine 的第一篇文章。主要是来介绍虚拟机。原文在这里
介绍
在本教程中,我将教你如何编写可以运行汇编语言程序的虚拟机(VM),例如我的朋友 2048 或 Roguelike。如果你知道如何去编程,但是想更加深入理解计算机内部以及理解你的编程语言是如何工作的,那么这个项目非常适合你。写一个你自己的 VM 可能听起来十分恐怖,但是我保证你会发现那非常简单。
大概有 250 行 C 代码(作者瞎说,其实大概要 500 行 c 代码)。你仅仅需要懂得如何去读 C/C++ 代码以及知道如何做二进制运算。要构建和运行代码,你需要使用 Unix 系统(包括macOS)。一些 Unix API 用于配置终端输入和显示,但这些并不是主代码必不可少的。 (欢迎 Windows 支持的贡献。)
Note:项目中的每一段代码都将被完整地显示和解释,因此你可以确定没有遗漏任何内容。
什么是虚拟机
一个虚拟机是一个像电脑的程序。这个程序模拟出一个 CPU 和一些其他硬件,并允许他们运行二进制,读写内存以及与 I/O 设备交互,就像一个物理机。更重要的是,它可以理解你编写的机器语言。
虚拟机尝试模拟的计算机硬件数量取决于其用途。一些虚拟机被用来复制一些特定的计算机,比如视频游戏模拟器。大多数人没有 NES,但我们仍然可以通过模拟程序中的 NES 硬件来玩 NES 游戏。这些仿真器必须忠实地重新创建原始设备的每个细节和主要硬件组件。
其他虚拟机不必像任何真正的计算机那样组成完全!这主要是为了使软件开发更容易。想象一下,你想要创建一个在多个计算机体系结构上运行的程序。 VM 可以提供一个标准平台,为所有这些平台提供可移植性。不需要在不同的 CPU 上写不同汇编的程序,你仅仅需要用不同的汇编语言写一个小的 VM 程序。然后,每个程序仅需要以 VM 的汇编语言编写一次,就 OK 了。
Note:编译器通过将标准高级语言编译为多个 CPU 架构来解决类似的问题。 VM 创建一个标准 CPU 架构,该架构在各种硬件设备上进行模拟。编译器的一个优点是它相对于 VM 没有运行时开销。即使编译器做得很好,编写一个针对多个平台的新版本也很困难,因此 VMs 在这里仍然很有用。在实践中,VMs 和编译器在各个级别混合。
Java 虚拟机(JVM)是一个非常成功的例子。 JVM 本身是一个中等大小的程序,不大,程序员可以对其进行理解。这使得可以为包括手机在内的数千种设备编写。一旦 JVM 在新设备上实现,任何编写的 Java,Kotlin 或 Clojure 程序都可以在其上运行而无需修改。唯一的成本是 VM 本身的开销以及机器的进一步抽象。大多数时候,这是一个非常好的权衡。
一个虚拟机想要提供相似的好处,不需要很大或者无处不在。老的电视游戏经常使用小的 VMs 提供一些简单的脚本系统。
VMs 在安全或者隔离地执行代码方面非常有用。在这方面的一个应用是「垃圾回收(garbage collection)」。在 C/C++ 上面实现一个自动的垃圾回收并不是一个简单的事情,因为一个程序不能看到它自己的栈和变量。然而,一个 VM 可以使运行的程序“暴露”出来,可以观察到在栈中的所有内存引用。
另一个例子:以太坊智能合约(Ethereum smart contracts)
智能合约是由区块链网络中的每个验证节点执行的小程序。这需要节点操作员在他们的机器上运行由完全陌生人编写的程序,而且没有任何事先仔细检查它们的机会。防止这些程序做恶意的事情,他们跑在一个 VM 里面。不能接近文件系统、网络等。以太坊也是使用 VM 时可移植性比较高的应用。因为,以太坊节点可以在多种计算机和操作系统上运行,使用 VM 可以编写智能合约,而无需考虑它们运行的许多平台。