关于lua 5.1源码分析与相关有用摘要

lua 5.1 的代码在线浏览 http://www.lua.org/source/5.1/


关于lua代码的阅读顺序,来自于 http://www.reddit.com/comments/63hth/ask_reddit_which_oss_codebases_out_there_are_so/c02pxbp


Recommended reading order:

  • lmathlib.c, lstrlib.c: get familiar with the external C API. Don't bother with the pattern matcher though. Just the easy functions.
  • lapi.c: Check how the API is implemented internally. Only skim this to get a feeling for the code. Cross-reference to lua.h and luaconf.h as needed.
  • lobject.h: tagged values and object representation. skim through this first. you'll want to keep a window with this file open all the time.
  • lstate.h: state objects. ditto.
  • lopcodes.h: bytecode instruction format and opcode definitions. easy.
  • lvm.c: scroll down to luaV_execute, the main interpreter loop. see how all of the instructions are implemented. skip the details for now. reread later.
  • ldo.c: calls, stacks, exceptions, coroutines. tough read.
  • lstring.c: string interning. cute, huh?
  • ltable.c: hash tables and arrays. tricky code.
  • ltm.c: metamethod handling, reread all of lvm.c now.
  • You may want to reread lapi.c now.
  • ldebug.c: surprise waiting for you. abstract interpretation is used to find object names for tracebacks. does bytecode verification, too.
  • lparser.c, lcode.c: recursive descent parser, targetting a register-based VM. start from chunk() and work your way through. read the expression parser and the code generator parts last.
  • lgc.c: incremental garbage collector. take your time.
  • Read all the other files as you see references to them. Don't let your stack get too deep though.

If you're done before X-Mas and understood all of it, you're good. The information density of the code is rather high.


个人感觉上面的参考阅读顺序一般,最好的方式是下载一份代码,编译然后调试,一个个看


关于lua 5.1的参考手册

http://www.codingnow.com/2000/download/lua_manual.html

http://www.cnblogs.com/whiteyun/archive/2009/08/08/1541985.html


lua的源码分析

http://www.pagefault.info/?p=462#more-462







--------------------------------------------------------------------------------------------------------------------------------------------------------------------

以下是个人分析lua 5.1代码的日志记录,我的分析顺序是先从大体lua程序执行流程入手,一般不纠结细节,先把lua整个流程

有了一个大概了解之后,再对有兴趣或者有依赖的细节进行深入了解。在这里暂时不分析lua的gc垃圾回收部分,这个是lua最

复杂的一块,再加上个人对gc技术没多大兴趣。


1.首先执行的是lua.c的main函数,这里要了解2个函数,1是lua_open,2是pmain函数。其实lua_open函数是luaL_newstate

   函数的宏定义,然后luaL_newstate其实最终调用的是lua_newstate,这里引出2个比较重要的数据结构,lua_State和global_State,

   其中global_State是全局只有1个的,lua_State是n个的,上面有一张图描述了具体lua_State结构比较重要的几个信息。关于

   global_State和lua_State的介绍,我在网上找一些相关资料(http://www.cnblogs.com/ringofthec/archive/2010/11/09/lua_State.html),

  如下:

  

global_State

一个lua虚拟机中只有一个, 它管理着lua中全局唯一的信息, 主要是以下功能

1. 内存分配策略及其参数, 在调用lua_newstate的时候配置它们. 也可以通过lua_getallocf和lua_setallocf随时获取和修改它

2. 字符串的hashtable, lua中所有的字符串都会在该hashtable中注册.

3. gc相关的信息. 内存使用统计量.

4. panic, 当无保护调用发生时, 会调用该函数, 默认是null, 可以通过lua_atpanic配置.

5. 注册表, 注意, 注册表是一个全局唯一的table.

6. 记录lua中元方法名称 和 基本类型的元表[注意, lua中table和userdata每个实例可以拥有自己的独特的元表--记录在table和userdata的mt字段, 其他类型是每个类型共享一个元表--就是记录在这里].

7. upvalue链表.

8. 主lua_State, 一个lua虚拟机中, 可以有多个lua_State, lua_newstate会创建出一个lua_State, 并邦定到global_state的主lua_State上.

global_State主要是管理lua虚拟机的全局环境.

 

lua_State

1. 要注意的是, 和nil, string, table一样, lua_State也是lua中的一种基本类型, lua中的表示是TValue {value = lua_State, tt = LUA_TTHREAD}

2. lua_State的成员和功能

    a. 栈的管理, 包括管理整个栈和当前函数使用的栈的情况.

    b. CallInfo的管理, 包括管理整个CallInfo数组和当前函数的CallInfo.

    c. hook相关的, 包括hookmask, hookcount, hook函数等.

    d. 全局表l_gt, 注意这个变量的命名, 很好的表现了它其实只是在本lua_State范围内是全局唯一的的, 和注册表不同, 注册表是lua虚拟机范围内是全局唯一的.

     e. gc的一些管理和当前栈中upvalue的管理.

     f. 错误处理的支持.

3. 从lua_State的成员可以看出来, lua_State最主要的功能就是函数调用以及和c的通信.

lua_State主要是管理一个lua虚拟机的执行环境, 一个lua虚拟机可以有多个执行环境.

   在lua_State结构里面,stack是需要我们重点了解的,lua会把执行需要的参数,函数调用等都放到stack里面,具体可以参考

   上面的图片。stack里面的所有元素都是StkId类型,而StkId则是Tvalue*的宏定义,TValue则是记录了元素的类型等重要信息,

   只有两个字段 Value value 和 int tt,其中value是类型的属性,然后tt则标识类型,下面是tt可能记录的类型

   #define LUA_TNONE               (-1)
   #define LUA_TNIL                0
   #define LUA_TBOOLEAN            1
   #define LUA_TLIGHTUSERDATA      2
   #define LUA_TNUMBER             3
   #define LUA_TSTRING             4
   #define LUA_TTABLE              5
   #define LUA_TFUNCTION           6
   #define LUA_TUSERDATA           7
   #define LUA_TTHREAD             8

   除了stack之外,我们还要了解  Instruction *savedpc 这个字段, Instruction 其实是一个 uint32类型,这个savedpc指向的是

   虚拟机执行的二进制指令,这是一个uint32的数值,当后面分析完lua脚本开始执行lua指令的时候就是从savaedpc开始读取

   指令执行的。具体在后面会详细介绍。

   另外还有CallInfo的字段成员,

   CallInfo *ci;  /* call info for current function */
   CallInfo *end_ci;  /* points after end of ci array*/
   CallInfo *base_ci;  /* array of CallInfo's */

   关于CallInfo结构,如下:

   /*
   ** informations about a call
   */
   typedef struct CallInfo {
     StkId base;  /* base for this function */
     StkId func;  /* function index in the stack */
     StkId top;  /* top for this function */
     const Instruction *savedpc;
     int nresults;  /* expected number of results from this function */
     int tailcalls;  /* number of tail calls lost under this entry */
   } CallInfo;


2.在lua.c的pmain函数里面,有几个函数是需要我们了解的,luaL_openlibs这个函数主要是做一些lua内部库函数的初始化工作

   做完初始化的工作之后,有两种模式,假如我们是以lua脚本的方式调用lua,则执行handle_script函数,另外一种就是交互模

   式的,这种就是在终端输入lua命令即时看到执行结果,这个则是执行dotty函数。因为我对lua的词法,语法分析,还有虚拟机

   等比较有兴趣,所以我选择去了解handle_script。


3.在handle_script函数里面主要有2个调用,luaL_loadfile 和 docall。

   luaL_loadfile主要实现的是 词法分析,语法分析,生成lua虚拟机指令,docall则是执行之前生成好的lua脚本虚拟机指令。


  调用关系

  luaL_loadfile -> lua_load -> luaD_protectedparser -> f_parser ->

  luaY_parser -> luaX_next 词法分析

                         -> chunk 语法分析,生成指令


下面我们先看看f_parser做了些什么

static void f_parser (lua_State *L, void *ud) {
  int i;
  Proto *tf;
  Closure *cl;
  struct SParser *p = cast(struct SParser *, ud);
  int c = luaZ_lookahead(p->z);
  luaC_checkGC(L);
  tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z,
                                                             &p->buff, p->name);
  cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L)));
  cl->l.p = tf;
  for (i = 0; i < tf->nups; i++)  /* initialize eventual upvalues */
    cl->l.upvals[i] = luaF_newupval(L);

  #这里很关键,这里是把luaY_parser分析完的Proto打包进Closure,然后放到lua_State的statck top里面
  setclvalue(L, L->top, cl);
  incr_top(L);
}


接下来我们再看看  luaY_parser

Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
  struct LexState lexstate;
  struct FuncState funcstate;
  lexstate.buff = buff;
  luaX_setinput(L, &lexstate, z, luaS_new(L, name));

  #open_func函数里面,会把Table和Proto push到lua_State的stack top里面

  open_func(&lexstate, &funcstate);

  funcstate.f->is_vararg = VARARG_ISVARARG;  /* main func. is always vararg */

  #词法分析

  luaX_next(&lexstate);  /* read first token */

  #语法分析并生成指令
  chunk(&lexstate);


  check(&lexstate, TK_EOS);
  close_func(&lexstate);
  lua_assert(funcstate.prev == NULL);
  lua_assert(funcstate.f->nups == 0);
  lua_assert(lexstate.fs == NULL);
  return funcstate.f;
}


static void open_func (LexState *ls, FuncState *fs) {
  lua_State *L = ls->L;
  Proto *f = luaF_newproto(L);
  fs->f = f;
  fs->prev = ls->fs;  /* linked list of funcstates */
  fs->ls = ls;
  fs->L = L;
  ls->fs = fs;
  fs->pc = 0;
  fs->lasttarget = -1;
  fs->jpc = NO_JUMP;
  fs->freereg = 0;
  fs->nk = 0;
  fs->np = 0;
  fs->nlocvars = 0;
  fs->nactvar = 0;
  fs->bl = NULL;
  f->source = ls->source;
  f->maxstacksize = 2;  /* registers 0/1 are always valid */
  fs->h = luaH_new(L, 0, 0);
  /* anchor table of constants and prototype (to avoid being collected) */
  sethvalue2s(L, L->top, fs->h); #把Table push到 lua_State的stack top
  incr_top(L);
  setptvalue2s(L, L->top, f); #把Proto push到 lua_State的stack top
  incr_top(L);
}


   下面我们再看看chunk,它会调用 statemenet做语法分析,指令生成,通常指令生成都会调用lcode.c里面的

   luaK_code

static int luaK_code (FuncState *fs, Instruction i, int line) {
  Proto *f = fs->f;
  dischargejpc(fs);  /* `pc' will change */


  /* 这里就是把指令I,也就是uint32的数据添加到Proto的code数组里面去,这个code数组在docall里面会赋值

     到lua_State的savedpc去执行指令的 */
  /* put new instruction in code array */
  luaM_growvector(fs->L, f->code, fs->pc, f->sizecode, Instruction,
                  MAX_INT, "code size overflow");
  f->code[fs->pc] = i;


  /* save corresponding line information */
  luaM_growvector(fs->L, f->lineinfo, fs->pc, f->sizelineinfo, int,
                  MAX_INT, "code size overflow");
  f->lineinfo[fs->pc] = line;
  return fs->pc++;
}


4.现在可以开始分析docall,调用关系如下

   docall -> lua_pcall -> f_call -> luaD_call  -> luaD_precall

                                                                             -> luaV_execute


   先看 luaD_precall,它主要是把 lua_Stat的stack的内容,比如 参数,函数,c调用等提取出来,并准备好等下的

   虚拟机执行,比如把 lua_State->savedpc = Proto.code


   接着看luaV_execute,它就负责从lua_State->savepc取出指令执行



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
993 年在巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro in Brazil)诞生了一门编程语言,发明者是该校的三位研究人员,他们给这门语言取了个浪漫的名字——Lua,在葡萄牙语里代表美丽的月亮。事实证明她没有糟蹋这个优美的单词,Lua语言正如它名字所预示的那样成长为一门简洁、优雅且富有乐趣的语言。 Lua从一开始就是作为一门方便嵌入(其它应用程序)并可扩展的轻量级脚本语言来设计的,因此她一直遵从着简单、小巧、可移植、快速的原则,官方实现完全采用ANSI C编写,能以C程序库的形式嵌入到宿主程序中。Lua的每个版本都保持着开放源码的传统,不过各版采用的许可协议并不相同,自5.0版(最新版是5.1) 开始她采用的是著名的MIT许可协议。正由于上述特点,所以Lua在游戏开发、机器人控制、分布式应用、图像处理、生物信息学等各种各样的领域中得到了越来越广泛的应用。其中尤以游戏开发为最,许多著名的游戏,比如Escape from Monkey Island、World of Warcraft、大话西游,都采用了Lua来配合引擎完成数据描述、配置管理和逻辑控制等任务。 作为一门过程型动态语言,Lua有着如下的特性:1、变量名没有类型,值才有类型,变量名在运行时可与任何类型的值绑定;2、语言只提供唯一一种数据结构,称为表(table),它类似key-value关联数组,可以用任何类型的值作为key和value。提供了一致且富有表达力的表构造语法,使得Lua很适合描述复杂的数据;3、函数是一等类型,支持匿名函数和正则尾递归(proper tail recursion);4、支持词法定界(lexical scoping)和闭包(closure);5、提供thread类型和结构化的协程(coroutine)机制,在此基础上可方便实现协作式多任务;6、运行期能编译字符串形式的程序文本并载入虚拟机执行;7、通过元表(metatable)和元方法(metamethod)提供动态元机制(dynamic meta-mechanism),从而允许程序运行时根据需要改变或扩充语法设施的内定语义;8、能方便地利用表和动态元机制实现基于原型(prototype-based)的面向对象模型;9、从5.1版开始提供了完善的模块机制,从而更好地支持开发大型的应用程序; Lua 的语法类似PASCAL和Modula但更加简洁,所有的语法产生式规则(EBNF)不过才60几个。熟悉C和ASCAL的程序员一般只需半个小时便可将其完全掌握。而在语义上Lua则与Scheme极为相似,她们完全共享上述的1、3、4、6点特性,Scheme的continuation与协程也基本相同只是自由度更高。最引人注目的是,两种语言都只提供唯一一种数据结构:Lua的表和Scheme的列表(list)。正因为如此,有人甚至称Lua为“只用表的Scheme”。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值