注:原文发在公司内部论坛上
Lua与C交互的栈是一个重要的概念。文章首先解释了为什么要引入Lua栈,然后对访问栈常用的API进行了总结,并使用这些API的注意事项,最后从Lua源代码来看栈的实现原理。
Lua栈概述
struct lua_State {
CommonHeader;
lu_byte status;
StkId top; /* 指向数据栈中,第一个可使用的空间*/
global_State *l_G;
CallInfo *ci; /* 保存着当前正在调用的函数的运行状态 */
const Instruction *oldpc;
StkId stack_last; /* 指向数据栈中,最后一个可使用的空间 */
StkId stack; /* 指向数据栈开始的位置 */
int stacksize; /*栈当前的大小,注意并不是可使用的大小*/
unsigned short nny;
unsigned short nCcalls;
lu_byte hookmask;
lu_byte allowhook;
int basehookcount;
int hookcount;
lua_Hook hook;
GCObject *openupval;
GCObject *gclist;
struct lua_longjmp *errorJmp;
ptrdiff_t errfunc;
CallInfo base_ci; /* 保存调用链表的第一个节点*/
};
Lua中所有的数据类型都是由结构体TValue来实现的,它把值和类型绑在一起,每个数据都携带了它自身的类型信息。用成员tt_保存数据的类型,成员value_用来保存数据值,它使用的一个联合体来实现的:
union Value {
GCObject *gc; /*gc指向一个对象,这些对象是需要垃圾回收的数据类型,比如table、string等*/
void *p; /* lua中的light userdata类型,实质上保存的就是一个指针 */
int b; /*boolean类型*/
lua_CFunction f;/*lua中light C functions(没有upvalue),即只是函数指针 */
double n; /*lua中的number类型*/
};
上面提到到数据栈是在函数stack_init中创建的(初始化虚拟机时调用的luaL_newstate,就是通过调用lua_newstate函数,lua_newstate调用f_luaopen函数,最后f_luaopen函数调用stack_init来初始化栈的),函数stack_init在lstate.c中实现,代码如下:
static void stack_init (lua_State *L1, lua_State *L) {
int i; CallInfo *ci;
/* 为数据栈分配空间,并且初始化lua_State与数据栈相关的成员*/
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;
/*初始化lua_State与调用链表相关的成员*/
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;
} 调用栈实质上用一个双向链表来实现的,链表中的每个节点是用一个CallInfo的结构体来实现,保存着正在调用的函数的运行状态。结构体CallInfo在lstate.h定义的,代码如下:
typedef struct CallInfo {
StkId func; /* 指向被调用的函数在栈中的位置*/
StkId top; /*指向被调用的函数可以使用栈空间最大的位置,即限定了调用一个函数可以栈空间的大小*/
struct CallInfo *previous, *next; /* 指向调用链表的前一个节点和下一个节点 */
short nresults; /* 当前被调用的函数期待返回结果的数量*/
lu_byte callstatus; /*用来标识当前调用的是C函数还是Lua函数*/
union {
struct { /* 当调用Lua调用函数时保存信息的结构体*/
StkId base;
const Instruction *savedpc;
} l;
struct { /*当调用C调用函数时保存信息的结构体*/
int ctx;
lua_CFunction k;
ptrdiff_t old_errfunc;
ptrdiff_t extra;
lu_byte old_allowhook;
lu_byte status;
} c;
} u;
} CallInfo;
总结
在Lua与C交互中间,我们可以直观想象,有一个叫栈的空间,无论是C还是Lua向对方需要什么数据,都首先需要对方把这数据压入到栈中,然后才能从栈中获取相应的数据。在Lua与C所有的数据交换中,栈中的每个元素都能保存任何类型的Lua值,并且这个栈是由Lua管理的。
ps:点这里有学习Lua与C交互的测试代码
深入理解Lua与C数据通信的栈,布布扣,bubuko.com
原文:http://blog.csdn.net/maximuszhou/article/details/21331819