分析Lua5.3.4源码,创建一个 Lua 状态机
luaL_newstate对用户的包装
/*lauxlib.c*/
LUALIB_API lua_State *luaL_newstate (void) {
lua_State *L = lua_newstate(l_alloc, NULL);
if (L) lua_atpanic(L, &panic);
return L;
}
lua_newstate:创建一个新的 Lua 状态机.
lua_newstate 接受2个参数,一个类型是lua_Alloc,另一个是void 。 f_luaopen调用stack_init初始化栈大小,默认为2LUA_MINSTACK,即 2* 20 = 40。 最后返回lua_State* L。
/*lstate.c*/
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
int i;
lua_State *L;/*'per thread' state*/
global_State *g;/*'global state', shared by all threads of this state*/
LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG)));
if (l == NULL) return NULL;
L = &l->l.l;
g = &l->g;
...
if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) {
/* memory allocation error: free partial state */
close_state(L);
L = NULL;
}
return L;
}
Lua 状态机中使用的内存分配器函数l_alloc :
C函数库中的malloc和free分别用于执行动态内存分配和释放,在stdlib.h中声明。
void *malloc ( size_t size );
malloc的作用是在堆内存的动态存储区中分配一个长度为size的连续空间,其参数是一个无符号整形数,返回值是一个指向所分配的连续存储域的起始地址的指针。
void free ( void *pointer );
由于堆内存区域是有限的,不能不限制地分配下去,而且一个程序要尽量节省资源,所以当所分配的内存区域不用时,就要释放它,以便其它的变量或者程序使用。这时就要用到free函数。
void *realloc (void *ptr, size_t new_size );
realloc函数用于修改一个原先已经分配的内存块(malloc )的大小,可以使一块内存的扩大或缩小。当起始空间的地址为空,即*ptr = NULL,则同malloc
/*lauxlib.c*/
static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
(void)ud; (void)osize; /* not used */
if (nsize == 0) {
free(ptr);
return NULL;
}
else
return realloc(ptr, nsize);
}
lua_newstate第一块申请的内存将用来存储global_State(全局状态机)和lua_State(主线程)实例。为了避免内存碎片的产生,利用一个LG结构,把分配lua_State和global_State的行为关联在一起。
/*
@@ LUA_EXTRASPACE defines the size of a raw memory area associated with
** a Lua state with very fast access.
** CHANGE it if you need a different size.
*/
#define LUA_EXTRASPACE (sizeof(void *))
/*
** thread state + extra space
*/
typedef struct LX {
lu_byte extra_[LUA_EXTRASPACE];
lua_State l;
} LX;
/*
** Main thread combines a thread state and the global state
*/
typedef struct LG {
LX l;
global_State g;
} LG;
global_State:管理lua虚拟机的全局环境,global_state 是不可见的。
1.stringtable:全局字符串表, 字符串池化,使得整个虚拟机中短字符串只有一份实例。
2.gc相关的信息
3.l_registry : 注册表(管理全局数据) ,Registry表可以用debug.getregistry获取。注册表
就是一个全局的table(即整个虚拟机中只有一个注册表),它只能被C代码访问,通常,它用来保存
那些需要在几个模块中共享的数据。比如通过luaL_newmetatable创建的元表就是放在全局的注册表中。
4.主lua_State, 在一个独立的lua虚拟机里, global_State是一个全局的结构,
而lua_State可以有多个。 lua_newstate会创建出一个lua_State, 绑在
lua_State *mainthread.可以说是主线程、主执行栈。
5.Meta table :tmname (tag method name) 预定义了元方法名字数组;mt 每一个Lua 的基本数
据类型都有一个元表。 global_mt可以用debug.getmetatable获取。
lua_State:管理一个lua虚拟机的上下文执行环境,也指代 lua 的一个线程。 一个lua虚拟机可以有多个执行环境.所有的lua C API 都是围绕这个状态机。它暴露给用户使用。
1.stack的管理, 包括管理整个栈和当前函数使用的栈的情况.每个线程拥有独立的数据栈以及函数调用栈。
2.CallInfo的管理
3.hook相关、错误处理设置
4.gc相关的信息
资料手册: Lua 5.3 参考手册