本文的目的应该理解为整个lua源码解析的一部分,当初做的目的是源于需要一次技术分享,而针对lua底层代码的运行时一个非常有意思的话题,换个说法,写的lua代码,是如果由lua虚拟机来运行的。
lua是由纯c代码实现的,写的相当精简,所以即使是作为一种学习c语言技巧的方法,也是很有必要的。正巧最近看了一一些关于编程方面的书,具体就是指DDD领域驱动设计,这书很有意思,以领域为中心,其他各个辅助系统是围绕核心领域来进行。那就可以把lua的解析以DDD来解析,而对于lua,其实可以先进行模块分解。
对c语言来说程序可以等价于数据结构+算法,而数据结构处于核心的位置。所以理解的基础,便是理解liua的数据结构,lua的虚拟机是如何组织数据的。而这其中又以lua_state最为重要,lua代码自己的注释是将lua_state当作协程来理解,就像c程序把一次执行当作一个线程。当然协程是种虚拟的概念,可以理解为在用户态的调度的运行实体,对应的线程和进程的区别对linux内核而言,仅仅是在于是否共享堆内存,其他部分进程和线程则具有相同的处理。
回到lua的解析,需要避免的是一开始就深入lua的各种细节,由于c语言的细节暴漏过多,会很容易陷入局部的细节而忽略了整体,导致只见树木不间森林。考虑到核心思想是理解lua是如何运行,所以编译原理部分,即将lua代码翻译成字节码,不深入涉及,只要知道,lua解释器会将lua代码,转换成对应的字节码来处理就可以了。具体到执行层面就可以用luac -l -l 来查看对应的字节码。
lua的指令集在文件 lopcodes.h,可以理解为是对冯诺依曼体系结构下,x86指令集的一种模拟。
lua指令的处理,在函数luaV_execute中。
在理解lua虚拟栈之前,可以思考一个这样的问题,如果写c/c++时不需要手动管理内存应该如何做呢?不在堆上开辟内存就可以了,只在栈上申请空间。这样每次退出{}代码块的时候,内存中的值就被内核自动回收了。这样做的问题就在于只要退出代码块就需要执行栈内存回收工作,那直接的解决方法就是不立即回收,而是由单独的GC算法来回收内存。
把这个过程抽象下,lua的做法可以理解为对操作系统的模拟,lua的所有数据确实都存在于栈中,而对栈上数据的清理也是由三色标记算法处理(lua5.3.5)。
如果从整个lua的执行角度来理解,那就要首先理解lua独立的执行环境的抽象,即所谓的闭包,也就是执行代码和对外部数据的以用的集合。一个闭包才是一个独立的执行环境。在代码中的表现,就是function end标记的代码块。而lua代码的入口,在解释时,会被创建一个名为main的闭包。