Lua本身是用C语言实现的,它是跨平台语言,得益于它本身的Lua虚拟机。
虚拟机相对于物理机,借助于操作系统对物理机器(CPU等硬件)的一种模拟、抽象,主要扮演CPU和内存的作用。
虚拟机的主要职责就是:执行字节码中的指令,管理全局状态(global_state)、数据栈(StackValue)和函数调用链状态(CallInfo)
可以理解成,lua虚拟机就是一个独立的空间,它会维护Lua的所有运行。
使用C函数,luaL_newstate 来创建。
会创建一个lua_State的结构体,该结构体就代表了一个Lua虚拟机。
一个进程中可以创建多个Lua虚拟机,即多个lua_State结构。
Lua虚拟机中是单线程实现,所以,多个创建的Lua虚拟机之间是相互独立的。
AppDelegate中
bool AppDelegate::applicationDidFinishLaunching()
{
;;....
// register lua module
auto engine = LuaEngine::getInstance();
;;...
}
这里在调用单例LuaEngine的时候,会创建LuaStack
LuaEngine中
bool LuaEngine::init(void)
{
_stack = LuaStack::create();
_stack->retain();
return true;
}
LuaStack中
bool LuaStack::init(void)
{
_state = lua_open();
luaL_openlibs(_state);
toluafix_open(_state);
// Register our version of the global "print" function
const luaL_Reg global_functions [] = {
{"print", lua_print},
{"release_print",lua_release_print},
{nullptr, nullptr}
};
;;....
}
其中成员变量_state的类型就是lua_State结构体,也就是Lua虚拟机。lua_open是函数luaL_newstate的宏定义。
同时可以得知,这里Lua虚拟机只有一个,这也是为什么在cocos2dx中需要严格按照规则来进行C++和Lua调用的原因。
lua虚拟机对象,本身是一个结构体。
它是一个lua线程的执行状态,所有的C api都是基于这个结构体的。
struct lua_State
{
CommonHeader;//#define CommonHeader
GCObject *next;
lu_byte tt;
lu_byte marked
lu_byte status;//虚拟机的错误状态码
StkId top;//栈顶元素所在位置的下一个位置,也就是栈上第一个空闲的位置
StkId base;//栈上,当前函数的基址(注意不是函数所在位置)
global_State* l_G;//全局表,环境章节再说
CallInfo* ci;//当前函数调用信息
const Instruction* savedpc;//当前函数的指令位置,指向待取指指令的地址
StkId stack_last;//栈最后一个位置的下一个位置
StkId stack;//寄存器数组的起始位置
CallInfo* end_ci;//函数调用信息数组的最后一个位置的下一个位置
CallInfo* base_ci;//函数调用信息数组首地址
int stacksize;//栈的大小
int size_ci;//函数调用信息数组大小
unsigned short nCcalls;//内嵌C调用层数
unsigned short baseCcalls;//唤醒协程时的内嵌C调用层数
lu_byte hookmask;
lu_byte allowhook;
int basehookcount;
int hookcount;
lua_Hook hook;
TValue l_gt;//Global表
TValue env;//环境表的临时位置
GCObject* openupval;//open状态的upvalues
GCObject* gclist;
struct lua_longjmp* errorJmp;//跳转信息单链表,实现try catch的功能,见函数章节
ptrdiff_t errfunc;//当前错误处理函数在栈上的索引
};
关于lua_State这里不过多阐述,感兴趣的可以看《lua设计与实现》这本书。
或者看看云风的《lua源码赏析》
或者看看这个博客
https://blog.csdn.net/yuanlin2008/category_1307277.html
global_State 全局状态机
如果说lua_State是面对外面表现的虚拟机对象,那么global_State才是背后真正的大佬,该结构体不对外开放,即无法用Lua公开的API获取到它的指针、句柄或引用。
它里面有对主线程(lua_State实例)的引用、有全局字符串表、有内存管理函数、有GC需要的相关信息以及一切Lua在工作时需要的工作内存等等
想要深入了解,同样可以查看《lua设计与实现》这本书。
Lua的堆栈是Lua和C++交互的基础,也就是说C++和Lua之间的数据类型交互都是通过这个虚拟栈进行完成的。
不管是C++需要获取Lua的数据还是需要传递数据给Lua,都需要先将这个数据压入栈中。
从索引来说,分为正向和逆向索引。其中-1永远表示栈顶,1永远表示栈底。
涉及到一些Lua入栈的函数(常用)。
函数 | 原型 | 说明 |
---|---|---|
lua_pushnumber | void lua_pushnumber (lua_State *L, lua_Number n) | 将数值类型n 压入栈中 |
lua_pushnil | void lua_pushnil (lua_State *L) | 将nil压入栈中 |
lua_pushboolean | void lua_pushboolean (lua_State *L, int b); | 将布尔类型压入栈中 |
lua_pushfstring | const char *lua_pushfstring (lua_State *L, const char *fmt, …) | 把一个格式化过的字符串压入堆栈,然后返回这个字符串的指针 |
lua_pushinteger | void lua_pushinteger (lua_State *L, lua_Integer n) | 将一个整型数字n压入栈中 |
lua_pushlstring | void lua_pushlstring (lua_State *L, const char *s, size_t len) | 把指针 s 指向的长度为 len 的字符串压栈 |
lua_pushstring | void lua_pushstring (lua_State *L, const char *s) | 把指针 s 指向的以零结尾的字符串压栈 |
lua_pushthread | int lua_pushthread (lua_State *L) | 把 L 中提供的线程压栈。 如果这个线程是当前状态机的主线程的话返回 1 |
lua_pushvalue | void lua_pushvalue (lua_State *L, int index) | 把堆栈上给定有效处索引处的元素作一个拷贝压栈 |
lua_pushlightuserdata | void lua_pushlightuserdata (lua_State *L, void *p) | 把一个 light userdata 压栈。 userdata 在 Lua 中表示一个 C 值。light userdata 表示一个指针。它是一个像数字一样的值:你不需要专门创建它,它也没有独立的 metatable ,而且也不会被收集(因为从来不需要创建)。只要表示的 C 地址相同,两个 light userdata 就相等 |
lua_pushcfunction | void lua_pushcfunction (lua_State *L, lua_CFunction f) | 将一个 C 函数压入堆栈。 这个函数接收一个 C 函数指针,并将一个类型为 function 的 Lua 值 压入堆栈。当这个栈顶的值被调用时,将触发对应的 C 函数 |
具体可以查看
https://www.w3cschool.cn/doc_lua_5_1/lua_5_1-index.html#lua_pushnumber
与之对应的取值为:
lua_to*(lua_State * L,栈中位置)取值
对应的出栈为:
lua_pop(lua_State * L,出栈个数)出栈
myName = “beauty girl”