我想构建一个虚拟机,有什么好的参考资料吗?[关闭]


22

我希望将虚拟机构建为平台独立的方式来运行一些游戏代码(基本上是脚本)。

我在游戏中意识到的虚拟机已经很老了:Infocom的Z-Machine,LucasArts的SCUMM,id Software的Quake 3。作为.net开发人员,我熟悉CLR,并研究了CIL指令以概述您在VM级别(与语言级别)上实际实现的内容。去年,我还涉猎了6502 Assembler

问题是,既然我想要¹实施它,我就需要更深入地研究。我知道有基于堆栈和基于注册的虚拟机,但我真的不知道哪种虚拟机在什么方面更好,以及是否有更多或混合的方法。我需要处理内存管理,确定哪些低级类型是VM的一部分,并且需要了解ldstr之类的东西为何以这种方式工作。

我唯一的参考书(除了Z-Machine之外)是CLI注释标准,但是我想知道是否有针对VM的更好,更通用/更基础的讲座?基本上像《龙书》,但适用于虚拟机?我知道Donald Knuth的计算机编程艺术,它使用基于寄存器的VM,但是我不确定该系列的适用性如何,尤其是因为它尚未完成?

澄清:目标是构建专用的VM。例如,Infocom的Z机包含用于设置背景颜色或播放声音的操作码。因此,我需要确定进入VM的OpCodes数量与使用脚本(语言TBD)并从中生成字节码的编译器的数量有关,但是为此,我需要了解自己的实际工作。


¹我知道,现代技术将使我能够即时解释高级脚本语言。但是那有什么乐趣呢?:)谷歌搜索也有点困难,因为如今虚拟机通常与VMWare型OS虚拟化相关联...


6
请注意,要使基于堆栈的计算机能够完全访问,则需要堆栈外部的内存,否则它只是PDA
棘手怪胎

1
第一个问题是:您想走多远?我从没看过SCUMM / SCUMMVM,但是假设这对图形事物有很高的了解,而CIL是...,所以您必须定义内存模型(基于堆栈的基于寄存器的,混合的,混乱的..)和操作码(即汇编程序指令),然后是VM的第一个版本是循环,do { switch(opcode) {case OP1: ... case OP2: ...} while (nextop);然后是编译器……然后,乐趣开始了-优化以使其真正起作用
johannes 2012年

3
尝试从实现简单的Forth运行时开始。
SK-logic

1
Quake 3虚拟机到底如何?
Ramhound 2012年

3
@Ramhound id技术引擎长期以来一直使用某种形式的内部虚拟化,否则这篇文章Wikipedia的信息可能会更好地解释。
Daniel B

Answers:


18

我先检查Lua。如果最终决定不自己推出自己的应用程序,那么这既可以作为示例实现,也可以作为非常实用的VM /语言使用。

源代码非常容易阅读,并且还有带注释的源代码。还有一些主要作者Roberto Ierusalimschy撰写的设计文件

最后,如果您选择使用它而不是自己使用它,您会发现它一直是游戏开发人员的最爱,并且有非常高性能的JIT实现

关于基于堆栈和基于寄存器的问题,我认为基于堆栈的VM更易于设计,但编译器可能更复杂。正如Iesualimschy论文所指出的那样,Lua是最早的基于寄存器的语言VM之一,但随后又出现了其他几种,最著名的是LLVM,Dalvik和一些现代的JavaScript VM。


2
关于堆栈与寄存器机器:我记得Parrot / Perl6开发人员的话:“构建基于寄存器的机器比较困难,但是我们受益于编译器方面的大量现有研究”(非文字)
johannes 2012年

+1 Lua具有出色的字节码实现和非常干净的设计,可用于学习VM。另外,您会发现许多人已经为自己的需求定制了Lua,如果您不想从头开始,那么它可以扩展。
CodexArcanum

仍在经历。开发人员关于VM的另一份出色的文档:inf.puc-rio.br/~roberto/talks/lua-ll3.pdf
Michael Stum

2

我目前没有任何特定资源可链接到您,但过去我研究过类似的话题,并发现Smalltalk VM也是很好的学习辅助工具。关于Smalltalk使用的字节码,有许多学术论文和文章,以及编写解释器和VM来使用该字节码。Google搜索smalltalk vm implementationsmalltalk bytecode interpreter应该产生大量阅读材料。

如果您想查看一些源代码或尝试实现,我建议使用Squeak或Pharo版本。

相关的语言/ VM Self也可能会让您感兴趣,因为Self基本上是Smalltalk,带有基于原型的对象(类似于JavaScript)。


0

我将从分析[脚本]源代码如何进入您的计算机或运行时环境开始。

如果您在HTML文档中有类似的东西,<a onclick="dosomething();">那么您将需要非常快速的编译器,在这种情况下,字节码执行速度并不重要。如果您的用例更接近Java / .NET(可以负担得起的全面编译),那么VM体系结构和字节码结构将更接近Java字节码或IL。

另一个标准是我所说的“粘性”。最初,脚本是作为胶水语言开发的-脚本仅定义了将各种本机功能(Perl,Python,Ruby,JS)连接在一起的方式。在这种情况下,当大多数代码是用语言本身编写的函数时,VM和字节码的有效性远不如Java / .NET重要。

我要使用的最后一个主要标准是语言的可扩展性。如果您打算向语言运行时添加许多在C ++中实现的本机对象/功能,则您的VM体系结构应该“方便”以便与C ++集成。例如:如果您打算以脚本C ++对象的形式公开它们,那么唯一的选择就是将引用计数作为堆管理(如Python,请参阅boost :: python作为集成示例)。如果您打算使用移动/压缩堆/ GC,那么情况将有所不同。Lua将本地内容添加到运行时的方式有些棘手(对于C ++开发人员)。

换句话说,尝试首先定义您的典型用例,这样会更容易建议您阅读什么。


1
现代的JavaScript编译器是相当复杂的,而且很好的问题有alwazs你如何把在生成的代码的优化得多。
约翰

JavaScript执行性能很重要。不是针对小的脚本,而是针对大型的JS繁重的网站,无论情况好坏,这些网站都构成了更受欢迎的网站的很大一部分。JS引擎使用JIT编译器是有原因的(V8甚至没有解释器,直接用于机器代码)。

@delnan:JS用例与Python完全不同。在Python中,当您需要类似光线跟踪算法的实现时,您将创建本机库并从脚本中调用它。它总是比任何JIT解决方案都快(或至少不慢)。在JS领域中,您没有本机代码那么奢侈,因此唯一的选择就是尝试使JS VM尽可能快。但是,再次,与价格。的评价“dosomethingnative()”中的HTML“<按钮的onclick =” dosomethingnative()“>在简单的解释可以在数量级比在V8更快。
C-微笑

@ c-smile我的意思完全是。

@delnan:但我的观点截然不同:分析常见的用例,然后您才能决定所需的VM体系结构,语言语法等。
c-smile
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.