使用C语言实现的干净、自包含的虚拟机,编译后代码大小在100到200K之间。

25
我正在寻找一个具有以下特点的虚拟机:
  • 小巧的编译代码占用空间(小于200K)。
  • 没有外部依赖。
  • 支持Unicode(或原始字符串)。
  • 清晰的代码/组织良好。
  • C(99)代码,不是C++。
  • C/Java风格的语法
  • 运算符/位运算:AND/OR等。
  • 线程支持
  • 通用/可移植字节码。即使在不同体系结构、不同字节序的计算机上编译,字节码也应该能够正常工作。
  • 基本语言支持而无需其他高级功能
  • 词法分析器/解析器与虚拟机独立。我将嵌入虚拟机到程序中,然后独立编译字节码。

目前我已经查看了LuaSquirrelNekoPawnIo、AngelScript等选项...唯一比较接近规范的是Lua,但语法很糟糕,没有位运算支持,并且代码风格普遍很糟糕。Squirrel和IO太庞大了。Pawn有问题,它虽然小巧,但字节码不跨平台,而且实现存在一些严重问题(例如,据我所知,字节码根本未经过验证,甚至包头也没有)。

我希望能找到一个合适的选项。

谢谢!

更新:JavaScript解释器是…解释器。这是基于字节码的虚拟机的一个VM问题,因此需要编译器/字节码虚拟机分离要求。JS是解释执行的,并且很少被即时编译(JIT)。我不一定需要JIT。另外,所有当前的ECMAScript解析器都只有很少代码。


5
“语法很可怕”这样的说法并不太有帮助,我们无法猜测出您真正想要的编程语言。 - Fred Foo
1
@larsmans:他表明了他想要的编程语言类型:“类似C/Java的语法”。 - user142162
就像我之前所说,C/Java式的语法。老实说,我不需要类继承/复杂的面向对象支持,但Lua的语法是不能接受的。我希望为那些有C或Java编程背景的开发者提供一些有吸引力的东西。就像Pawn/Squirrel的语法,但没有上述的“额外花哨”。 - user656208
1
看看V8(JavaScript)和tcc(ANSI C)。也许你可以将它们中的一个虚拟化... - pmg
好的,我可以给你展示一下我的虚拟机,我将其编写为脚本而不是程序。它仍在不断改进中,也许你可以帮我指个方向 -> https://github.com/assyrianic/C-Virtual-Machine - Nergal
显示剩余4条评论
7个回答

6
你说你已经查看了NekoVM,但没有提到为什么它不适合你。
它是用C编写的,而不是C ++,虚拟机的代码行数不到10kLOC,编译后的大小约为100kB,并且编译器是一个单独的可执行文件,可以生成可移植的字节码。 该语言本身具有类似C的语法、位运算符,并且不会产生线程问题。

这取决于我想要避免的 Boehm GC。(“无外部依赖。”)在我的审查中,alloc.c 中的 API 似乎不容易用其他东西替换。此外,据我所知,Boehm GC 的 tinygc 版本不支持必要的 API :( - user656208
由于NekoVM的嵌入式语言看起来很像Javascript,因此最好选择Google V8而不是NekoVM。考虑到文档、支持和现有使用V8(node.js)的虚拟机。 - Tim
1
@Tim V8不满足OP的其他要求。它是用C++编写的,并且编译后的代码大小要大得多(4MB)。 - ephemient
@ephemient 这完全取决于您如何使用V8以及使用什么,此外,如果您需要将VM与库链接起来,则几乎4 MB的差异相对较小。两个VM都是完全空的,除了内置类型之外。 关于要求,NekoVM和V8都不能满足几乎所有要求。只是想为任何看到您的答案并正在寻找类似解决方案的人添加一些内容,我认为V8是更好的选择。 :) - Tim
1
@Tim 我也喜欢 V8,但是 NekoVM 是纯 C 语言编写的,并且编译后只有约100KB,这对于原帖的作者来说非常重要。而 V8 的二进制文件大小远远达不到这个水平——4MB 的二进制文件大小仅仅是 V8,不包括其他库。 - ephemient

4

JerryScript:

  • 需要少于64KB的RAM
  • 大约160KB的二进制文件大小
  • 使用C99编写
  • 基于虚拟机(VM)
  • 有字节码预编译

IoT JavaScriptJerryScript与libuv(类似node.js风格)连接在一起-这可能更容易使用。

线程可能还没有达到您想要的状态。ECMAScript最近增加了关于在单独的线程上运行后台工作和共享跨线程缓冲区的功能,不确定在JerryScript中的情况如何-可能还没有实现,但谁知道呢-他们已经有了一个如何实现它的蓝图,也许并不遥远。


3

终于,在这么长的时间里,没有一个答案真正解决了问题。最终我选择了fork LUA。截至今天,没有符合上述要求的自包含VM存在......这真是遗憾;(

尽管如此,Pawn相当不错,只是代码有些问题。


5
你的Lua分支版本是自由可用的吗?或者你可以分享你所做的修改吗? - lhf
Soze,我也很感兴趣。 - Blub
1
根据它们各自的网站:Lua源代码包含大约20000行C代码,在Linux下,构建了所有标准Lua库的Lua解释器占用182K,而Lua库占用243K。Squirrel编译器和虚拟机总共只需要约7k行C++代码,并且仅会增加大约100kb-150kb的可执行文件大小。那么你为什么说Squirrel很“庞大”呢? - Graham Wheeler
在测试的时候,我认为情况并非如此,而且功能也不是与Lua并行的,即使Squirrel的语法更好。 - user656208
关于该模块:它从未被发布过,但如果我有时间,我可能会尝试将补丁提交到上游,或将其传递给其他人。不过很可能它们不会被接受。 LUA从来没有对将词法分析器和编译器与解释器分离的人持开放态度。此外,还存在字节码架构可移植性的问题。LUA没有一个大端/小端不可知的解释器。 - user656208

3
对于非常“基本”的东西:

http://en.wikibooks.org/wiki/Creating_a_Virtual_Machine/Register_VM_in_C

这只是一个对该主题的简短介绍,没有其他什么内容。

但是,它可能至少符合以下几个所需标准:

  • 编译后代码占用空间小(小于200K)...显然符合;
  • 没有外部依赖...确实;
  • 代码清晰/组织良好...确认;
  • C(99)代码,而不是C++...确认;
  • 类似于C / Java的语法...确认。

不在范围内。需要的是 C 语法,而不是实现语言。 - user656208

1

尝试使用EmbedVM。

http://www.clifford.at/embedvm/

http://svn.clifford.at/embedvm/trunk/

这是一个猜数字游戏的代码示例。编译器使用lex+yacc构建,语言为C:

global points;

function main()
{
    local num, guess;
    points = 0;
    while (1)
    {
        // report points
        $uf4();

        // get next random number
        num = $uf0();
        do {
            // read next guess
            guess = $uf1();
            if (guess < num) {
                // hint to user: try larger numbers
                $uf2(+1);
                points = points - 1;
            }
            if (guess > num) {
                // hint to user: try smaller numbers
                $uf2(-1);
                points = points - 1;
            }
        } while (guess != num);

        // level up!
        points = points + 10;
        $uf3();
    }
}

没有任何线程支持。但是虚拟机中没有全局状态,因此可以轻松在同一进程中运行多个副本。

API很简单。通过回调访问VM RAM。您的主循环重复调用embedvm_exec(vmdata),它执行单个操作并返回。

VM占用空间很小,已在8位微控制器上使用过。


不幸的是,它没有UTF-8支持,还有其他一些问题。虚拟机本身还是相当不错的。 - user656208

0
一种选择是使用一些最小的东西并进行扩展。mini-vm 代码行数不到200行,包括注释,它有一个自由许可证(MIT),它是用C语言编写的。开箱即用,它支持0个操作,但很容易扩展。所附带的示例编译器只是一个简单的计算器。但是,你可以很容易地想象添加比较、分支、内存访问和监管呼叫来实现你想要的功能。易于扩展的虚拟机对于开发特定领域语言尤其有用,并且让多种语言针对你的 mini-vm 版本非常简单,除了必须实现多个编译器(或移植它们。QuakeC 编译器只是 lcc,非常容易重新定位)。
线程支持必须是一个扩展,核心 VM 在多处理器 pthread 场景下(重量级线程)无法正常运行。奇怪的是,mini-vm 可以为每个重量级线程设置一个程序计数器(PC),但会在所有使用相同上下文的线程之间共享寄存器。运行单独的上下文将是线程安全的。

我跳过回答关于语言的要求,因为问题开始询问裸机VM。但同时要求C / Java类似的语法,不确定如何解决这种冲突,除非指出这种冲突。


0

Spidermonkey不符合我提供的标准。 - user656208
@soze 它错过了哪些? - Null Set
如果你阅读了Spidermonkey的功能集和要求,你会意识到它不是一个字节码虚拟机,编译后的代码大小肯定超过200K,没有任何字节码和编译器之间的分离(因为根本就没有...它是一个代码解析器/词法分析器实现),而且它也不是基础版,使用案例完全不同。 - user656208
@soze 啊,我以为解释器没问题,因为你提到了Lua作为候选。我不知道你可以将Lua脚本预编译为字节码。 - Null Set
2
Lua在运行之前总是被编译成字节码。与许多其他“解释”语言(例如Perl和Python)一样,当给定文本作为输入程序时,运行时会自动执行此操作。您会发现很少有解释器直接从文本或解析树中工作,因此,即使SpiderMonkey也具有内部字节码表示,但它似乎没有定义得那么好。 - ephemient
好的,JavaScript和其他一些语言可以在支持JIT的引擎中转换为本地代码(称之为“金属级字节码”;))。有些引擎也可以使用字节码进行转换,比如.NET或Java。我刚刚发现Lua不支持跨平台字节码。如果字长改变,你就会很倒霉。:( 所以在这方面它仍然比Pawn差。 - user656208

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接