马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
大家好,窝是目录(窝在无名侠的无名团队里边叫目录),这是应该是窝的第一个帖子吧。
之前看到网上关于HLLVM(高级语言虚拟机)的东西很少,
so 一下子就忍不住了,
由于不敢在百度贴吧这种坑逼网站发布
之前也了解过fishC论坛,感觉挺不错的,至少没那么多伸手党,也没发生过类似于 Quarrel war Ⅰ 一类的武装冲突。
so 决定了,就在这发。
(psⅠ:先说好了,JIT不关我事蛤。
(psⅡ:由于fishc论坛帖子图片的支持性不是很好,所以这篇文章没有图片。
======================================== The divider Ⅰ ========================================
原理
首先,我们从原理来讲。
所谓高级语言虚拟机,就是解释器的一种。而解释器恐怕大家都并不陌生吧:像咱经常用的翻译软件,根据输入的英文,找到对应的中文释义并输出(具体过程可能会更复杂);或者是Shell:根据输入的命令(行?)找到对应的op(操作)并执行。
从上面的栗子中我们可以发现解释器的基本运行过程(流程):
输入->(保存输入)->匹配操作->(保存结果)->输出
也就是说,解释器的运行流程,是一个交互的过程(不管是人机交互还是文件交互什么的)。
辣嚒,看似神秘高深的HLLVM就变得很简单了。
辣嚒,我们从解释器的运行流程开始剖析。
首先是输入,几乎所有虚拟机都是有数据载入的,我们先从常规的(主流的)载入方式说起,像现在的一些高级语言虚拟机,如JavaVM,PythonVM(pyc载入)等等,都是从磁盘载入的,辣嚒就应该考虑载入位置的问题,
现在有两种方案,一种是虚拟机初始化时载入;另一种是Runtime(运行时)载入;执行个字节码读取一个。很显然,第二种果断干掉,为啥?原因很简单,毕竟并不是所有人都买得起SSD硬盘的(包括楼楼,被DELL坑得好惨),机械硬盘在某种实时性很强的地方是有很大的问题的,一是读取需要旋转磁盘盘片,二是读取的速度不稳定;还有就是跳转指令如LG_IMP_IF_FALSE或LG_LOOP_IF_FALSE很难实现,so 不用我再详细的解释了吧。
再来就是保存输入,这个不用怎么说,用C++实现很简单,一行std::vector<char> ByteCode;砸下去就完事了,但是用C语言的就尴尬了,必须自己来实现跳表(我觉得也不对吧,毕竟有标准库这种东西),如果是Python……嗯……嗯……(羡慕)。
再来就是匹配操作,所谓匹配操作是怎么一回事呢?匹配操作就是根据特定的标记符Byte code(字节码)得出所需做的操作。那么,我们把保存在虚拟机内存空间的字节码看作一个字节流,然后用一个大循环来分别解释这些字节码,
While(explain(read_bytecode()));
然后就是指令分派,为了让字节码和操作一一对应。这里有有两种方案可以考虑,一种是用一个巨大的函数里边装一个巨大的switch来分派;另一种是用函数指针数组的方式。这种说法可能有些绕口,简单来说就是用函数指针来实现多态性(一种方法,多种操作)。好吧我知道这么解释对不用OOP的人来说和没解释没什么两样。就是说用字节码指令号来作为函数指针的偏移地址(看过窝的一个内存专题的应该知道)typedef void(*_op)(void) runop;
runop op_list[X] = {……};
但由于是用函数指针会降低访问速度,并且内联不能与函数指针配合。
辣嚒我们用一个函数(由于效率问题,最好用内联函数。之所以不用宏替换是为了可读性方面考虑)来解释字节码指令(虽然那个巨大的解释函数窝差点没写吐)bool explain(const char bytecode) {
switch(bytecode) {
……
}
}
然后就是保存结果,窝这里所说的保存结果并不是指运算结果如Bytecode:
PUSH_CONST 1 (4)
PUSH_CONST 2 (9)
POP_ADD
>> 13
这样的,
窝所说的运行结果是指日志文件,如出错日志、GC日志、对象活动日志一类的文件,它们通常以xml文件的形式表现,而有些大型的工业级的VM日志甚至由数据库管理。(网上那个说用html文件的形式表现的ZZ粗来,我保证不把你屎打粗来)
最后,输出应该好理解,一般的输出就是指Basic output(大概是这个吧),这里所说的基本输出并不是指BIOS,而是指像输出流:控制台/文件输出什么的。(就是利用标准库输出)辣嚒,来一段示例:printf(“Hello_World!”); //C
std::cout << “Hello_World!”; //C++
qDebug() << “Hello_World!”; //Qt
System.out.PrintIn(“Hello_World!”); //Java
print(“Hello_World!”) #Python
(print “Hello_World!”) #(Lisp)
各种语言的标准输出。
当然有些是图形界面输出这我就不说了。
(psⅢ:虚拟机如果加上图形界面,可移植性会大幅度降低,所以javaEE的GUI组件相应速度才如此之慢,所以PHP才被实现为数据处理语言,当然Python那个靠Qt作弊的不算啊<Qt万岁!>
【一个开头忘说了但很重要的东西】
(ps:注意窝下面说的话很可能不正确,甚至是谬论,你只用知道里面的技术就行了,因为窝并不是很懂计算机史。
最早的虚拟机,是以模拟真实的计算机为目的的,它们与计算机硬件(架构)非常相似,如DosBox等。而随着虚拟机的技术愈发完善,更Light的(注意我这里说的并不是运行速度)、更抽象的虚拟机也逐渐被提出,如PythonVM。人们发现:线程虚拟机并不需要完全模拟计算机,只需要符合 冯·诺依曼 提出的 储存->运行 的规则就行,于是新的计算模型被提出——基于堆栈的虚拟机,而现在,寄存器机和栈机各自的优势并不相同,so 产生了较大的分歧(照现在看来在HLLVM这块应该是基于堆栈大获全胜)。
那么,该如何选择架构?这个可以从两种架构所对应的指令集来说。首先说栈的指令集:基于栈的指令集代码紧凑,操作数通常<=1,占用较小;而基于寄存器的指令集,由于是多地址,所以通常占用较大。所以,选栈吧骚年。
(ps:以上【一个开头忘说了但很重要的东西】全凭个人主观臆断瞎逼逼,不喜勿喷
======================================== The divider Ⅱ ========================================
实现
好了,原理部分讲完了,接下来该说说具体实现了。
其实挺简单的
就拿窝写的第一个虚拟机来说吧(由于版面问题,现给出部分源码
首先是运行环境,//==========Domo==========
//std::vector<char> bytecode; //字节流
//std::vector<long double> const_pool;
unsigned long bytecodepre;
std::stack<long double> op_stack;
//字节码
enum {
op_push,
op_pop,
op_add,
……
op_print,
}:
char bytecode[] = { //DeBug
op_push, 1,
op_push, 2,
op_push, 3,
op_add,
op_add,
op_print,
};
//由于需要提高运行效率,所以部分需要宏定义:
#define Read_bytecode() bytecode[bytecodepre]
#define bytecodepre_addo() bytecodepre++
#define bytecodepre_subo() bytecodepre—
#define bytecodepre_add(X) bytecodepre+X
#define bytecodepre_sub(X) bytecodepre-X
//主体框架
bool explain(char bytecode) {
bytecodepre_addo();
switch(int bytecode) {
case op_push: {
stack.back_push(const_pool[Read_bytecode()]);
} return true;
……
……
}
};
int main(int argc, char** argv) {
while(explain(Read_bytecode()));
return 0;
};
好吧窝腾过来的时候偷懒没分文件
(我都好久没碰代码了,再加上被VS惯养了,所以恐怕会有不少错误,所以如果有问题,请在评论区留言,谢谢。
由于作者时间有限,只能完成这么多,所以更高级的虚拟机的设计就留在进阶篇讲解。
欲深入研究HLLVM(高级语言虚拟机),请参阅以下几本书(按排名):
《Python源码剖析》注意是Python源码剖析,但讲的是Python虚拟机
《深入理解Java虚拟机》——JVM高级特性欲最佳实践
《虚拟机的设计与实现C/C++》一个物理学家转程序员的经典著作,但技术较老
-没事还可以去看看Lua虚拟机源码啥的。
Ps:由于本人文笔极差(从文中大量使用括号就能看粗来),所以请各位大大多多支持哈。
同时,我将尽快的将我的各种填好的坑移植到鱼C(OSChina、网易):
(ps:妈蛋,自己挖的坑,含泪也得填完啊。
【解释器原理】进击的高级语言虚拟机(进阶)
主要衔接入门篇,实现一个(更高级的)虚拟机的对象机制、多线程、栈帧、模块动态加载机制。
【无名学院-目录】内存专题
从C/C++的各种内存操作符讲起,深入剖析指针与内存,了解其本质,并逐步实现一个智能指针的封装和一个微型内存管理机制(就是窝最新的虚拟机的那套机制。
Java的引用也要讲一些
嗯……这篇文章窝也数了数。嗯,着实为本人平生良心之作,满满的都是干货,绝对没水。所以……嗯…… @无名侠 @无名侠 @无名侠 /傲娇 /傲娇 /傲娇
最后,我想说的是
励志创造人生,编程改变命运。
谢谢。 |