1.Redis服务端在启动的时候会初始化lua的运行环境,Redis是C写的,所以实际上就是C与LUA的交互。
2.Lua 是通过一个虚拟堆栈来与C交互的,如下图。
请注意红色数字,代表通信顺序:
1) C想获取Lua的myName字符串的值,所以它把myName放到Lua堆栈(栈顶),以便Lua能看到
2) Lua从堆栈(栈顶)中获取myName,此时栈顶再次变为空
3) Lua拿着这个myName去Lua全局表查找myName对应的字符串
4) 全局表返回一个字符串”beauty girl”
5) Lua把取得的“beauty girl”字符串放到堆栈(栈顶)
6) C+可以从Lua堆栈中取得“beauty girl”,也就是这位美丽的Lua小姐的名字了~
这段是借鉴网友的比喻,很形象。
一、接下来就来说一下Redis的启动过程中,LUA执行环境的初始化
lua 与 C 交互的API ,都是操作这个虚拟栈来实现交互的。
LUA API : https://blog.csdn.net/yhhwatl/article/details/9303675,https://blog.csdn.net/zhukangle/article/details/52791355
在Redis启动的int main(int argc, char **argv) -> initServer() -> scriptingInit(1),真正执行环境初始化的地方就是scriptingInit,接下来我们分析下这个函数里面的内容。
Redis启动过程中lua环境的初始化工作就是这么个流程。
二、redis执行eval,evalsha命令的流程
evalCommand() -> evalGenericCommand() ,先看下eval命令的格式
然后看下源码的流程
Lua 调用函数API
void lua_call (lua_State *L, int nargs, int nresults);
int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);
int lua_cpcall (lua_State *L, lua_CFunction func, void *ud);
lua_call 调用函数,参数nargs指定函数参数个数,参数nresults指定返回值个数。首先,被调函数必须在栈中;其次,函数参数必须是按从左往右的顺序入栈的;函数调用时,所有函数参数都会弹出堆栈。函数返回时,其返回值入栈(第一个返回最最先入栈)。
lua_pcall 以保护模式调用函数,如果发生错误,捕捉它,并将错误消息压入栈,然后返回错误码。
lua_cpcall 以保护模式调用C函数func,参数ud指针指向一个用户自定义数据。
根据LUA脚本返回值的类型,转成对应C类型,然后输出给伪客户端