Evan Phoenix谈Rubinius:虚拟机内幕面面观

数周以前,Evan Phoenix加盟EngineYard,在那里他每天的工作中有一半时间花在Rubinius项目上。我们对Evan进行了一次采访,了解项目项目的进展和虚拟机的内部情况,并且(在采访的第二部分)探讨了最新的一些特性的实现方式。

其它的Ruby实现,比如说JRuby、XRuby、Gardens Point Ruby.NET、IronRuby,要么针对已有的虚拟机,要么和Ruby 1.x一样使用C语言编写,而Rubinius另辟蹊径,它从Smalltalk虚拟机处获得灵感,尤其是在Squeak Smalltalk之中。Squeak是用它自己Smalltalk的一个子集写成,这个子集名叫Slang,它基本上是使用了Smalltalk语法的C语言代码,并加入了一定的限制。Rubinius的目标之一,就是使用这种方式贯穿项目始终,它使用的语言名叫Garnet(以前被称为“Cuby”),目前仍处于开发阶段。Evan就目前进展解释说:

这仍然是我计划在近期要做的事情,而不是今后的。之前,我们遇到了许多需要处理的问题,而且我们还没有时间回来着手Garnet(Cuby的新名字)上的工作。目前我们尚未找到什么特别的问题,但我相信我们会发现的。

第一眼看过去Garnet语言和Ruby长得没什么区别,但是里面一些东西代表的语义进行了一些修改。

举例来说,在Garnet语言中,“d = c.to_ref”这样的代码看似调用了c对象的to_ref方法,但实际上Garnet会把这段代码翻译成“d = &c”这样的C代码。我们可以把它看作一个非常先进的C预处理器,试图尽可能将自己和对应的C语言构造映射起来。我们的想法就是写出看起来像 Ruby的代码,而行为则和C语言类似。

目前,虚拟机的基础部分是使用C语言编写的,也采取源自Smalltalk的方式。Evan就此解释了基础实现(primitives)的本意:

他们是可以在Ruby中调用的小块C语言代码,用来实现Ruby无法完成的功能。一个很好的例子是给一个对象分配 (内存)。在系统后端,与垃圾回收机制交互,为特定对象的分配准备出足够的空间。这样的操作在Ruby之中无法实现,这种操作方式只存在于系统的最底层, 也就是所谓的基础实现。

Rubinius的基础实现与Smalltalk的基础实现机制很相近。如果方法指定了原生(primitive)的操作号,当方法被执行时,则会调用原 生(primitive)操作而不是常规的Ruby代码。如果primitive操作执行失败(primitive自身报告执行已经失败),那么Ruby 代码就会作为后备的行为被执行。Ruby代码可以使用如抛出包含操作失败原因的异常的方式,将参数转换并再次进行尝试,或者做一些其他的操作。

为了展现它实际运行的方式,这里附一段rubinius的primitive操作代码作为例子:

  def fixnum_size(_ = fixnum)
<<-CODE
stack_push(I2N(sizeof(int)));
CODE
end

接着Evan给我们解释了一遍代码的运行过程:

primitives和指令使用一系列有趣的格式来保持其操作的简单性。所有的操作都是Ruby方法,而程序体是 包含C代码的字符串。在编译的时候,这些文件被执行,然后某些代码会调用每一个方法,收集C的代码并且在其它C代码的#include部分声明。这样做的 主要原因是基础实现和指令在C语言的声明中可以包装为一个大的switch表达式,如果使用手工的方式来完成无疑将会是非常痛苦的。

而且,它 提供给我们一些程序预处理的能力。例如,在这里(代码示例),你看见我们在定义参数的原生类型(primitive)fixnum_size的时候使用了 一些小的技巧。首先,代码的关键是在调用的方法的代码执行前会自动注册C语言代码段“POP(self, FIXNUM_P)”。调用并且输出C语言代码的方法将会正确的声明和写入这些代码。用这样的形式,我们可以方便基础实现(primitives)的编 写。

但是我声明,目前并非所有基础实现(primitives)都使用了这样的形式。很快,我们将会进行审查,将所有的实现都统一使用这种形式。

可以看出,随着可用的Garnet越来越多之后,Ruby和C语言之间的界限会变得模糊,原有的界限将在今后发生改变。对于更深一层的软件栈来说,标准的类库将使用Ruby来实现。

目前,虚拟机的核心(实际上就是操作码)和基础实现(primitives)都是使用C语言编写的。基础实现(primitives)是我想在Garnet中实现的第一个特性。两者的垃圾回收机制也都同样是使用C语言编写(虽然他们所占的的代码量很少)。

除了这些,所有的一切都是用Ruby实现的。所有解释器都可以解析命令行参数字符串(就像rubinius -d -v这样开启调试和警告信息)。所有代码的运行时环境可以轻松的操作(修改),因为它们也都是Ruby的。
Rubinius虚拟机在过去一段时间内发展很快。为了保持发展的势头并吸引更多的开发者,在更多的开发者开始贡献代码还有更多的测试人员开始加入到Rubinius项目中之后,他们需要得到更多项目相关的信息。
目前,IRC讨论组 #rubinius是得到信息的最好渠道(当然,直接读源代码也是非常有效的方式)。讨论组的一些历史记录信息可以 在线找到。Evan详细的阐述了增加项目透明度的计划:
我们尽力去保持整个过程的透明度。目前,项目的IRC讨论组是最主要的交流方式,并且我们设法将IRC的记录整合到站点http://rubini.us之上。同时我们鼓励用户使用我们在 http://rubini.us上的论坛来反馈问题。论坛拥有RSS的feed读取方式,并且大多数的开发者可以通过订阅RSS来加入讨论(尽管我不得不承认,有些功能还有待真正实现)。

关于如何使得项目更加透明,通过各种方式提出的建议,我都非常欢迎。我们可以一直考虑满足用户提出的规格。一旦我们拥有一个完全规范的套件,其余的功能将很容易的添加在其中。

感兴趣的读者可以继续关注这段采访的下一个章节,下个章节探讨了Rubinius调试器、垃圾回收、ObjectSpace和线程方面的一些最新特性的实现方式。

查看英文原文:Evan Phoenix on Rubinius - VM Internals Interview

你可能感兴趣的:(Evan Phoenix谈Rubinius:虚拟机内幕面面观)