lua源码:开始

文件根据实现功能的不同,可以分为四部分:

 

1虚拟机运转的核心功能

lapi.c            C语言接口
lctype.c        C标准库中ctype相关实现
ldebug.c      Debug接口
ldo.c            函数调用以及栈管理
lfunc.c         函数原型及闭包管理
lgc.c            垃圾回收
lmem.c        内存管理接口
lobject.c      对象操作的一些函数
lopcodes.c  虚拟机的字节码定义
lstate.c        全局状态机
lstring.c       字符串池
ltable.c        表类型的相关操作
ltm.c            元方法
lvm.c            虚拟机

lzio.c            输入流接口

 

2源代码解析以及预编译字节码

lcode.c         代码生成器
ldump.c       序列化预编译的 lua 字节码
llex.c            词法分析器
lparser.c       解析器

lundump.c   还原预编译的字节码

 

3内嵌库

lauxlib.c        库编写用到的辅助函数库
lbaselib.c      基础库
lbitlib.c         位操作库
lcorolib.c      协程库
ldblib.c         Debug库
linit.c            内嵌库的初始化
liolib.c          IO库
lmathlib.c     数学库
loadlib.c       动态扩展库管理
loslib.c         OS库
lstrlib.c         字符串库
ltablib.c        表处理库 

4可执行的解析器,字节码编译器

lua.c              解释器
luac.c            字节码编译器
lua核心部分仅包括lua虚拟机的运转。lua虚拟机的外在数据形式是一个lua_State结构体,取名State大概

意为lua虚拟机的当前状态。

 

阅读源代码的次序(从外到内)

首先、阅读外围的库是如何实现功能扩展的,这样可以熟悉Lua公开API。不必陷入功能细节。
然后、阅读API的具体实现。

Lua对外暴露的API可以说是一个对内部模块的一层封装,这个层次尚未触及核心,但可以对核心代码有个初步的了解。
之后、可以开始了解LuaVM的实现。
接下来就是分别理解函数调用、返回,string 、table 、metatable 等
如何实现。
debug 模块是一个额外的设施,但可以帮助你理解 Lua 内部细节。
最后是 parser 等等编译相关的部分。

垃圾收集将是最难的部分,可能会花掉最多的时间去理解细节。

 

Lua的C函数以堆栈的形式和Lua虚拟机交换数据,由一系列API从L中取出值,经过一番处理,压回L中的堆栈。

Lua栈给C函数留的默认空间很小,默认情况下只有20。当你要在Lua的栈上留下大量值时,务必用luaL_checkstack扩展堆栈。因为处于性能考虑,Lua和栈有关的API都是不检查栈溢出的情况的。

在实现上,UserData只是对象结构从TString换成了Udate

 

Lua使用table作为统一的数据结构。 用 table来表示Lua中的一切数据结构是Lua语言的一大特色。 为了效率,Lua的官方实现,又把table的储存分为数组部分和哈希表部分。 

Table按照Lua语言的定义,需要实现四种基本操作:读、写、迭代和获取长度。Lua中并没有删除操作,而仅仅是把对应键位的值设置为nil

Table的数组部分操作和C语言数组没有什么不同,不需要特别处理。 

Lua实现复杂数据结构,大量依赖给table附加一个元表(metatable)来实现。

 

你可以很容易的创建出一个Lua虚拟机对象,不同的Lua虚拟机之间的工作是线程安全的,因为一切和虚拟机相关的内存操作都被关联到虚拟机对象中,而没有利用任何其它共享变量。 

Lua的虚拟机核心部分,没有任何的系统调用,是一个纯粹的黑盒子,正确的使用Lua,不会对系统造成任何干扰。这其中最关键的一点是,Lua让用户自行定义内存管理器,在创建Lua虚拟机时传入,这保证了Lua的整个运行状态是用户可控的。

从Lua的使用者的角度看,global_state 是不可见的。我们无法用公开的API取到它的指针,也不需要引用它。global_State里面有对主线程的引用,有注册表管理所有全局数据,有全局字符串表,有内存管理函数,有GC需要的把所有对象串联起来的相关信息,以及一切Lua在工作时需要的工作内存。 通过lua_newstate创建一个新的lua虚拟机时,第一块申请的内存将用来保存主线程和这个全局状态机。 Lua的实现尽可能的避免内存碎片,同时也减少内存分配和释放的次数。它采用了一个小技巧,利用一个LG结构,把主线程 lua_State 和 global_state 分配在一起。 

Lua中的数据可以这样分为两类:值类型和引用类型。值类型可以被任意复制,而引用类型共享一份数据,由GC负责维护生命期。Lua使用一个联合 union value来保存数据。 

Lua的程序运行是以线程为单位的。每个Lua线程可以独立运行直到自行中断,把中断的信息留在状态机中。每条线程的执行互不干扰,可以独立延续之前中断的执行过程。Lua的线程和系统线程无关,所以不会为每条Lua线程创建独立的系统堆栈,而是利用自己维护的线程栈,内存开销也就远小于系统线程。 

lua是用longjmp处理与c互相函数调用的问题。

 

如果说C++式的面向对象编程是把一组函数绑定到特定数据类型上的话,那么闭包可以说是把一组数据绑定到特定函数上。 闭包是函数和upvalue的结合体。



Lua中的函数调用有两种,一种是标准的函数调用,它会需要生成新的一层调用栈,执行函数流程,然后弹出调用栈返回。另一种叫做尾调用,它是对标准函数调用的优化。尾调用不生成新的调用栈,而不复用当前的。在大多数函数式编程语言中,都需要对尾调用做特别优化。因为函数式语言特别依赖函数的层层调用,甚至用尾调用的方式来做循环。传统方式每次函数调用都需要生成新的栈帧,容易造成栈溢出。 尾调用可以看作C中的goto。

参考:

《lua源码赏析》

如何学习 Lua VM 的源码?https://www.zhihu.com/question/20617406

《 Structure and Interpretation of Computer Programs 》

 

posted @ 2018-07-03 15:09  _raindrop  阅读(5840)  评论(0编辑  收藏  举报