关于Lua中基本数据类型的底层实现方式,本篇是IState。

lState

lua_State

在lState.h中定义了lua_State的结构体,这是每个 Lua 函数都会接受的表示当前状态的结构,最主要的成员包括运行栈 stack, 栈用于表示函数调用、传递参数及返回值:

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
32
33
34
35
36
37
/*
** 'per thread' state
*/
struct lua_State {
CommonHeader; /* */
lu_byte status; /* 解析容器的,用于记录中间状态 */

global_State *l_G; /* 全局状态机 */

/*调用栈:调用栈的信息管理*/
CallInfo *ci; /* call info for current function 当前函数的运行信息 */
CallInfo base_ci; /* CallInfo for first level (C calling Lua) 调用栈的头部指针 */

/* 数据栈:栈指针地址管理 */
StkId top; /* first free slot in the stack 指向线程栈的栈顶 */
StkId stack_last; /* last free slot in the stack 线程栈的最后一个位置 */
StkId stack; /* stack base 栈的指针,当前运行的位置 */

GCObject *gclist; /* GC列表 */
const Instruction *oldpc; /* last pc traced 在当前thread 的解释执行指令的过程中,指向最后一次执行的指令的指针 */
struct lua_State *twups; /* list of threads with open upvalues */
struct lua_longjmp *errorJmp; /* current error recover point */
UpVal *openupval; /* list of open upvalues in this stack */

/* Hook 相关管理 - 服务于debug模块 */
lua_Hook hook;
ptrdiff_t errfunc; /* current error handling function (stack index) */
int stacksize;
int basehookcount;
int hookcount;
lu_byte hookmask;
lu_byte allowhook;

/* 跟C语言通信 管理 */
unsigned short nny; /* number of non-yieldable calls in stack */
unsigned short nCcalls; /* number of nested C calls */
};

GCUnion

1
2
3
4
5
6
7
8
9
10
11
12
/*
** Union of all collectable objects (only for conversions)
*/
union GCUnion {
GCObject gc; /* common header */
struct TString ts;
struct Udata u;
union Closure cl;
struct Table h;
struct Proto p;
struct lua_State th; /* thread */
};

global_state

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/*
** 'global state', shared by all threads of this state
** lua全局状态机
** 作用:管理全局数据、全局字符串表、内存管理函数
*/
typedef struct global_State {
const lua_Number *version; /* pointer to version number 版本号 */

/* 内存管理 */
lua_Alloc frealloc; /* function to reallocate memory Lua的全局内存分配器 */
void *ud; /* auxiliary data to 'frealloc' 分配器的userdata */

lu_mem totalbytes; /* number of bytes currently allocated - GCdebt */
l_mem GCdebt; /* bytes allocated not yet compensated by the collector */
lu_mem GCmemtrav; /* memory traversed by the GC */
lu_mem GCestimate; /* an estimate of the non-garbage memory in use */
TValue l_registry;
unsigned int seed; /* randomized seed for hashes */
lu_byte currentwhite;
lu_byte gcstate; /* state of garbage collector */
lu_byte gckind; /* kind of GC running */
lu_byte gcrunning; /* true if GC is running */
GCObject *allgc; /* list of all collectable objects */
GCObject **sweepgc; /* current position of sweep in list */
GCObject *finobj; /* list of collectable objects with finalizers */
GCObject *gray; /* list of gray objects */
GCObject *grayagain; /* list of objects to be traversed atomically */
GCObject *weak; /* list of tables with weak values */
GCObject *ephemeron; /* list of ephemeron tables (weak keys) */
GCObject *allweak; /* list of all-weak tables */
GCObject *tobefnz; /* list of userdata to be GC */
GCObject *fixedgc; /* list of objects not to be collected */

Mbuffer buff; /* temporary buffer for string concatenation */

/* 字符串管理 */
stringtable strt; /* hash table for strings */

/* GC管理 */
unsigned int gcfinnum; /* number of finalizers to call in each GC step */
int gcpause; /* size of pause between successive GCs */
int gcstepmul; /* GC 'granularity' */

/* 线程管理 */
struct lua_State *mainthread; /* 主线程 */
struct lua_State *twups; /* list of threads with open upvalues 闭包了当前线程变量的其他线程列表 */

/* 错误处理 */
lua_CFunction panic; /* to be called in unprotected errors */
TString *memerrmsg; /* memory-error message */

/* 虚函数表 */
TString *tmname[TM_N]; /* array with tag-method names 预定义方法名字数组 */
struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types 每个基本类型一个metatable */
} global_State;

创建栈结构:

lua_newstate主要用于创建一个主线程栈结构,分配lua_State和global_State。

对外通过lua_State结构暴露给用户,而global_State挂载在lua_State上。

通过这种结构,每个线程会独立维护自己的线程栈和函数栈。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
int i;
lua_State *L;
global_State *g;
/* 分配一个lua_State结构的内容块 */
LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG)));
if (l == NULL) return NULL;
L = &l->l.l;
g = &l->g;
L->next = NULL;
L->tt = LUA_TTHREAD;
g->currentwhite = bitmask(WHITE0BIT);
L->marked = luaC_white(g);

/* 初始化一个线程的栈结构数据 */
preinit_thread(L, g);
g->frealloc = f;
g->ud = ud;
g->mainthread = L;
g->seed = makeseed(L);
g->gcrunning = 0; /* no GC while building state */
g->GCestimate = 0;
g->strt.size = g->strt.nuse = 0;
g->strt.hash = NULL;
setnilvalue(&g->l_registry);
luaZ_initbuffer(L, &g->buff);
g->panic = NULL;
g->version = NULL;
g->gcstate = GCSpause;
g->gckind = KGC_NORMAL;
g->allgc = g->finobj = g->tobefnz = g->fixedgc = NULL;
g->sweepgc = NULL;
g->gray = g->grayagain = NULL;
g->weak = g->ephemeron = g->allweak = NULL;
g->twups = NULL;
g->totalbytes = sizeof(LG);
g->GCdebt = 0;
g->gcfinnum = 0;
g->gcpause = LUAI_GCPAUSE;
g->gcstepmul = LUAI_GCMUL;
for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL;
if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) {
/* memory allocation error: free partial state */
close_state(L);
L = NULL;
}
return L;
}

/*
** preinitialize a thread with consistent values without allocating
** any memory (to avoid errors)
*/
static void preinit_thread (lua_State *L, global_State *g) {
G(L) = g; /* 将global_State结构挂载在lua_State上 #define G(L) (L->l_G) */
L->stack = NULL;
L->ci = NULL;
L->stacksize = 0;
L->twups = L; /* thread has no upvalues */
L->errorJmp = NULL;
L->nCcalls = 0;
L->hook = NULL;
L->hookmask = 0;
L->basehookcount = 0;
L->allowhook = 1;
resethookcount(L);
L->openupval = NULL;
L->nny = 1;
L->status = LUA_OK;
L->errfunc = 0;
}

销毁栈结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* 关闭lua栈 */
LUA_API void lua_close (lua_State *L) {
L = G(L)->mainthread; /* only the main thread can be closed */
lua_lock(L);
close_state(L);
}

/* 释放lua栈结构 */
static void close_state (lua_State *L) {
global_State *g = G(L);
luaF_close(L, L->stack); /* close all upvalues for this thread 释放栈结构的upvalues */
luaC_freeallobjects(L); /* collect all objects 释放全部对象 */
if (g->version) /* closing a fully built state? */
luai_userstateclose(L);
luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size);
luaZ_freebuffer(L, &g->buff);
freestack(L);
lua_assert(gettotalbytes(g) == sizeof(LG));
(*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */
}

数据栈与调用栈:

数据栈结构由一个StkID结构存储。lua中的数据分为值类型和引用类型。引用类型需要GC机制管理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
union Value {
GCObject *gc; /* collectable objects 存放所有需要垃圾回收的类型的对象 */
void *p; /* light userdata 存放轻量用户数据 */
int b; /* booleans */
lua_CFunction f; /* light C functions 存放一个方法 */
lua_Integer i; /* integer numbers 存放int数字 */
lua_Number n; /* float numbers 存放float数字 */
};

struct lua_TValue {
TValuefields;
};

// #define TValuefields Value value_; int tt_

typedef TValue *StkId; /* index to stack elements */

调用栈存放在CallInfo中,以数组的形式存放在虚拟机的对象里。其中CallInfo中的ci->func指向正在执行的函数在数据栈上的位置l->top(正在调用的函数一定位于数据栈上。)

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
/*
** Information about a call.
** When a thread yields, 'func' is adjusted to pretend that the
** top function has only the yielded values in its stack; in that
** case, the actual 'func' value is saved in field 'extra'.
** When a function calls another with a continuation, 'extra' keeps
** the function index so that, in case of errors, the continuation
** function can be called with the correct top.
*/
typedef struct CallInfo {
StkId func; /* function index in the stack func 指向正在执行的函数在数据栈上的位置 */
StkId top; /* top for this function */
struct CallInfo *previous, *next; /* dynamic call link */
union {
struct { /* only for Lua functions */
StkId base; /* base for this function */
const Instruction *savedpc;
} l;
struct { /* only for C functions */
lua_KFunction k; /* continuation in case of yields */
ptrdiff_t old_errfunc;
lua_KContext ctx; /* context info. in case of yields */
} c;
} u;
ptrdiff_t extra;
short nresults; /* expected number of results from this function */
lu_byte callstatus;
} CallInfo;

栈初始化和释放:

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
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); // 默认栈的大小为40
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; // 栈顶默认到35

/* 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->top + 20的位置
L1->ci = ci;
}

/* 释放栈内存结构 */
static void freestack (lua_State *L) {
if (L->stack == NULL)
return; /* stack not completely built yet */
L->ci = &L->base_ci; /* free the entire 'ci' list */
luaE_freeCI(L); /* 一个释放循环链表的过程 */
luaM_freearray(L, L->stack, L->stacksize); /* free stack array */
}

栈初始化由f_luaopen函数调用,对它的调用出现在lua_newstate中:

1
2
3
4
5
if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) {
/* memory allocation error: free partial state */
close_state(L);
L = NULL;
}

栈扩容操作:

Lua会通过lua_checkstack函数去检查栈空间的大小。初始化分配栈大小的时候是分配了40个StkId,栈顶留5个buf空间,所以栈顶是35个,这部分函数在lapi.c中实现:

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
/*
** to be called by 'lua_checkstack' in protected mode, to grow stack
** capturing memory errors
*/
/* 针对lua_state进行扩容 */
static void growstack (lua_State *L, void *ud) {
int size = *(int *)ud;
luaD_growstack(L, size);
}

LUA_API int lua_checkstack (lua_State *L, int n) {
int res;
CallInfo *ci = L->ci;
lua_lock(L);
api_check(n >= 0, "negative 'n'");
if (L->stack_last - L->top > n) /* stack large enough? */
res = 1; /* yes; check is OK */
else { /* no; need to grow stack */
int inuse = cast_int(L->top - L->stack) + EXTRA_STACK;
if (inuse > LUAI_MAXSTACK - n) /* can grow without overflow? */
res = 0; /* no */
else /* try to grow stack */
res = (luaD_rawrunprotected(L, &growstack, &n) == LUA_OK);
}
if (res && ci->top < L->top + n)
ci->top = L->top + n; /* adjust frame top */
lua_unlock(L);
return res;
}

对lua_state进行扩容的growstack函数内部调用了ldo.c中的luaD_growstack函数:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void luaD_growstack (lua_State *L, int n) {
int size = L->stacksize;
if (size > LUAI_MAXSTACK) /* error after extra size? */
luaD_throw(L, LUA_ERRERR);
else {
int needed = cast_int(L->top - L->stack) + n + EXTRA_STACK;
int newsize = 2 * size; /* 栈空间扩充到目前size的2倍 */
if (newsize > LUAI_MAXSTACK) newsize = LUAI_MAXSTACK; /* 如果扩充后的size大于最大size则将其设置为最大size */
if (newsize < needed) newsize = needed; /* 如果2倍不够直接设置为目标大小 */
if (newsize > LUAI_MAXSTACK) { /* stack overflow? */
luaD_reallocstack(L, ERRORSTACKSIZE);
luaG_runerror(L, "stack overflow");
}
else
luaD_reallocstack(L, newsize);
}
}

/* 重新分配一块statck内容,并且进行拷贝 */
void luaD_reallocstack (lua_State *L, int newsize) {
TValue *oldstack = L->stack;
int lim = L->stacksize;
lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK);
luaM_reallocvector(L, L->stack, L->stacksize, newsize, TValue);
for (; lim < newsize; lim++)
setnilvalue(L->stack + lim); /* erase new segment */
L->stacksize = newsize;
L->stack_last = L->stack + newsize - EXTRA_STACK;
correctstack(L, oldstack);
}

/* 拷贝到新的栈结构上 */
static void correctstack (lua_State *L, TValue *oldstack) {
CallInfo *ci;
UpVal *up;
L->top = (L->top - oldstack) + L->stack;
for (up = L->openupval; up != NULL; up = up->u.open.next)
up->v = (up->v - oldstack) + L->stack;
for (ci = L->ci; ci != NULL; ci = ci->previous) {
ci->top = (ci->top - oldstack) + L->stack;
ci->func = (ci->func - oldstack) + L->stack;
if (isLua(ci))
ci->u.l.base = (ci->u.l.base - oldstack) + L->stack;
}
}