Lua源码剖析(二)核心结构体

一. 前言

  无论是和静态语言交互,还是内部的API,永远少不了lua_State,这就是Lua最核心的栈结构体,也是本文的重点介绍对象。由于该结构体涉及到了众多功能,因此这里不会详细到每一个变量的作用都展开说明,而是站在脚本语言设计和开发者的角度看,设计一个栈结构体,需要哪些部分,留下一个比较系统性的框架概念即可。

二. 核心结构体分析

  写过Lua的demo程序,都不会陌生这个API

1
lua_State* luaL_newstate()

  在使用Lua时,首先需要调用该API生成栈,之后再加载文件或是直接填写逻辑。如果说Lua虚拟机执行指令模拟的是CPU的运作,那么Lua栈模拟的就是可执行文件加载在内存的堆栈的操作。在Lua内部参数的传递是通过Lua栈,Lua与C等外部进行交互的时候也是使用的栈。lua_State中保存了大量信息,直接看结构体源码可能就会陷进去了,所以我们可以先分个类。

  设想如果是自己写一个保存脚本执行信息的栈结构体,那可能需要以下几方面内容

  • 全局存储信息,起到类似全局变量之类的效果
  • 栈信息,毫无疑问需要上下限索引、计数等
  • 函数相关,用链表或者其他形式去把所有函数存储起来并且能够获取到
  • GC相关,用于垃圾回收
  • 函数跟踪调试,给出对外的接口以便于调试代码以及打印情况
  • 异常抛出处理,用于处理异常情况

  而Lua的栈结构体基本也就包括了这几块。首先看看整体的源码,对照刚刚所说的应该就能看懂个大概了,下面会详细就各个方面进行更详细的说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Lua 5.1,本文均为此版本,后续不在标注
/*
** `per thread' state
*/
struct lua_State {
CommonHeader;
lu_byte status;
StkId top; /* first free slot in the stack */
StkId base; /* base of current function */
global_State *l_G;
CallInfo *ci; /* call info for current function */
const Instruction *savedpc; /* `savedpc' of current function */
StkId stack_last; /* last free slot in the stack */
StkId stack; /* stack base */
CallInfo *end_ci; /* points after end of ci array*/
CallInfo *base_ci; /* array of CallInfo's */
int stacksize;
int size_ci; /* size of array `base_ci' */
unsigned short nCcalls; /* number of nested C calls */
lu_byte hookmask;
lu_byte allowhook;
int basehookcount;
int hookcount;
lua_Hook hook;
TValue l_gt; /* table of globals */
TValue env; /* temporary place for environments */
GCObject *openupval; /* list of open upvalues in this stack */
GCObject *gclist;
struct lua_longjmp *errorJmp; /* current error recover point */
ptrdiff_t errfunc; /* current error handling function (stack index) */
};

2.1 全局信息

  全局信息成员变量如下,TValue在数值一讲中详谈。

1
2
3
4
5
struct lua_State {
global_State *l_G;
TValue l_gt; /* table of globals */
TValue env; /* temporary place for environments */
}

  对应结构体global_State主要包含几方面的全局内容

  • 字符串的哈希表(下一讲中会详细说明)
  • 原表、上值等
  • 内存分配函数
  • GC相关的众多链表及相应变量(后续在GC一文详细展开说明)
  • 全局统计信息:总内存、正在使用的内存估算

  源码如下,可以对照着过一遍,有个大致印象就好,后面会用到的地方会具体说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
** `global state', shared by all threads of this state
*/
typedef struct global_State {
stringtable strt; /* hash table for strings */
lua_Alloc frealloc; /* function to reallocate memory */
void *ud; /* auxiliary data to `frealloc' */
lu_byte currentwhite;
lu_byte gcstate; /* state of garbage collector */
int sweepstrgc; /* position of sweep in `strt' */
GCObject *rootgc; /* list of all collectable objects */
GCObject **sweepgc; /* position of sweep in `rootgc' */
GCObject *gray; /* list of gray objects */
GCObject *grayagain; /* list of objects to be traversed atomically */
GCObject *weak; /* list of weak tables (to be cleared) */
GCObject *tmudata; /* last element of list of userdata to be GC */
Mbuffer buff; /* temporary buffer for string concatentation */
lu_mem GCthreshold;
lu_mem totalbytes; /* number of bytes currently allocated */
lu_mem estimate; /* an estimate of number of bytes actually in use */
lu_mem gcdept; /* how much GC is `behind schedule' */
int gcpause; /* size of pause between successive GCs */
int gcstepmul; /* GC `granularity' */
lua_CFunction panic; /* to be called in unprotected errors */
TValue l_registry;
struct lua_State *mainthread;
UpVal uvhead; /* head of double-linked list of all open upvalues */
struct Table *mt[NUM_TAGS]; /* metatables for basic types */
TString *tmname[TM_N]; /* array with tag-method names */
} global_State;

2.2 栈信息

  既然是Lua栈,那么自然需要保存栈底和栈顶,以及入栈内容的相关信息,相关源码如下。这里注释都很清楚,没有什么难懂的地方,就是通过几个索引位置让各种栈操作可以方便的执行。

1
2
3
4
5
6
7
8
9
typedef TValue *StkId;  /* index to stack elements */

struct lua_State {
StkId top; /* first free slot in the stack */
StkId base; /* base of current function */
StkId stack_last; /* last free slot in the stack */
StkId stack; /* stack base */
int stacksize;
}

2.3 函数

  多个函数的入栈和调用等操作当然都需要保存在栈内,所以需要函数相关的一系列成员变量

1
2
3
4
5
6
7
8
struct lua_State {
CallInfo *ci; /* call info for current function */
const Instruction *savedpc; /* `savedpc' of current function */
CallInfo* end_ci; /* points after end of ci array*/
CallInfo* base_ci; /* array of CallInfo's */
int size_ci; /* size of array `base_ci' */
unsigned short nCcalls; /* number of nested C calls */
}

  lua_State中的CallInfo保存当前调用函数的信息,也提供了一系列指针方便索引栈、函数顶、其他函数。定义如下:

1
2
3
4
5
6
7
8
9
10
11
/*
** 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;

  lua_StateCallInfo初始化主要在stack_init()函数中执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static void stack_init(lua_State *L1, lua_State *L)
{
int i;
CallInfo *ci;
/* initialize stack array */
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE, TValue);
L1->stacksize = BASIC_STACK_SIZE;
for (i = 0; i < BASIC_STACK_SIZE; i++)
setnilvalue(L1->stack + i); /* erase new stack */
L1->top = L1->stack;
L1->stack_last = L1->stack + L1->stacksize - EXTRA_STACK;
/* initialize first ci */
ci = &L1->base_ci;
ci->next = ci->previous = NULL;
ci->callstatus = 0;
ci->func = L1->top;
setnilvalue(L1->top++); /* 'function' entry for this 'ci' */
ci->top = L1->top + LUA_MINSTACK;
L1->ci = ci;
}

2.4 GC

  GC的主要相关变量在global_State中定义,但是除此之外仍有两个特殊变量

1
2
3
4
struct lua_State {
GCObject* openupval; /* list of open upvalues in this stack */
GCObject* gclist;
}

  这里之所以会存在一个gclist,是因为lua_State本身也是一个可以GC的对象,为了方便GC统一处理因此做了这种统一化的存在,而不适合放在global_State之中。openupval的话,是因为专属于这个栈,因此必须放在这儿。关于上值,在后续基础数据类型中会有详细说明。

2.5 Hook

  对于脚本语言,我们也希望能够提供一种跟踪、调试的方法,对于Lua来说,其实现就是Hook系列。该部分内容比较有趣,后续会单独展开详谈。

1
2
3
4
5
6
7
struct lua_State {
lu_byte hookmask;
lu_byte allowhook;
int basehookcount;
int hookcount;
lua_Hook hook;
}

三. 总结

  本文对lua_State进行了概要介绍,希望是有一个提纲式的了解,掌握栈的基础构造,细节内容后面慢慢填充即可。

参考文献

  1. Lua-Source-Internal

  2. 《A No Frills Introduction to Lua 5.1 VM Instructions》

  3. http://lua-users.org/wiki/LuaSource

  4. 《The Implementation of Lua 5.0》

坚持原创,坚持分享,谢谢鼓励和支持