skynet源码阅读<1>--lua与c的基本交互
阅读skynet的lua-c交互部分代码时,可以看到如下处理:
struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1));
那么,问题来了:skynet_context是如何作为upvalue与C函数绑定在一起的呢?这里以luaopen_skynet_core(lua_State *L)为例:
int luaopen_skynet_core(lua_State *L) {
luaL_checkversion(L);
luaL_Reg l[] = { /*注册函数部分略去*/ { NULL, NULL } };
luaL_newlibtable(L, l);
lua_getfield(L, LUA_REGISTRYINDEX, "skynet_context");
struct skynet_context *ctx = lua_touserdata(L,-1);
if (ctx == NULL) {
return luaL_error(L, "Init skynet context first");
}
luaL_setfuncs(L,l,1);
return 1;
}
这里先通过luaL_newlibtable创建一张表T(函数指针表l并未实际注册到表T中,只是分配了相应大小的空间),然后从全局索引表中取出事先注册的skynet_context,接着调用luaL_setfuncs(L,l,1),将函数表注册到T中。注册时每个函数都会从栈顶取出指定数目的元素(这里为1)作为upvalue。到lua源码中看看这部分具体的实现如何:
/*
** set functions from list 'l' into table at top - 'nup'; each
** function gets the 'nup' elements at the top as upvalues.
** Returns with only the table at the stack.
*/
LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
luaL_checkstack(L, nup, "too many upvalues");
for (; l->name != NULL; l++) { /* fill the table with given functions */
int i;
for (i = 0; i < nup; i++) /* copy upvalues to the top */
lua_pushvalue(L, -nup);
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
lua_setfield(L, -(nup + 2), l->name); /*-(nup+2)位置的元素正是之前创建的表T;这里以函数entry的name为key,向其注册闭包*/
}
lua_pop(L, nup); /* remove upvalues */
}
可以看到扫描每个函数entry时,都会将push到栈顶的upvalues全部复制新的一份出来到栈顶,接着通过lua_pushcclosure创建C闭包,并注册到表T中去。函数表注册完毕后,再清理掉栈顶的upvalues。此时栈顶就是完成注册的表T了。至此,每个C闭包已经关联了skynet-context作为它们的upvalue。至于skynet_context,则是加载lua服务时,会创建C服务snlua(负责加载lua虚拟机),此时会将context实例注册到lua虚拟机的全局注册表中。细节见service_snlua.c,这里不再赘述。
先看下C闭包的定义(lobject.h):
typedef struct CClosure {
ClosureHeader;
lua_CFunction f;
TValue upvalue[1]; /* list of upvalues */
} CClosure;
除了ClosureHeader之外(包括upvalue的个数,CommonHeader的对象类型tt_,标记mark_等),可以看到C闭包包含一个函数指针f和一个upvalue数组。upvalue数组的实际大小是根据upvalue的个数来创建的。这里要做的事情就是把upvalue拷进C闭包中,如下(lapi.c):
LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
lua_lock(L);
if (n == 0) {
setfvalue(L->top, fn);
}
else {
CClosure *cl;
api_checknelems(L, n);
api_check(L, n <= MAXUPVAL, "upvalue index too large");
cl = luaF_newCclosure(L, n);
cl->f = fn;
L->top -= n;
while (n--) {
setobj2n(L, &cl->upvalue[n], L->top + n);
/* does not need barrier because closure is white */
}
setclCvalue(L, L->top, cl);
}
api_incr_top(L);
luaC_checkGC(L);
lua_unlock(L);
}
这里将栈上的元素copy到cl的upvalue数组中,然后再将cl设置到栈顶。之前被这里的while循环绕了一下,设n为N0,减一之后为N1,那么n--的运算结果为N0,但是循环中n的值则是N1,二者并不一样,逻辑是正确的。
那么剩下的最后一个问题是,在C函数中,是如何取到相关的upvalue的?首先是通过lua_upvalueindex(1)拿到对应的upvalue的索引:
#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i))
在本例中,进入lua_touserdata,可以看到index2addr的实现:
static TValue *index2addr (lua_State *L, int idx) {
CallInfo *ci = L->ci;
if (idx > 0) {
TValue *o = ci->func + idx;
api_check(L, idx <= ci->top - (ci->func + 1), "unacceptable index");
if (o >= L->top) return NONVALIDVALUE;
else return o;
}
else if (!ispseudo(idx)) { /* negative index */
api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index");
return L->top + idx;
}
else if (idx == LUA_REGISTRYINDEX)
return &G(L)->l_registry;
else { /* upvalues */
idx = LUA_REGISTRYINDEX - idx;
api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large");
if (ttislcf(ci->func)) /* light C function? */
return NONVALIDVALUE; /* it has no upvalues */
else {
CClosure *func = clCvalue(ci->func);
return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : NONVALIDVALUE;
}
}
}
在upvalue的处理这块,先是还原upvalue在C闭包中的index,然后从当前的CallInfo中取到闭包,拿到upvalue返回。
skynet源码阅读<1>--lua与c的基本交互的更多相关文章
- skynet 源码阅读笔记 bootstrap.lua
最近几周粗略看了 skynet 代码的 C 部分.遇到很多知识点以前只是知道,但并不十分了解,所以这是一个学习的过程. 从 main 函数开始,闷头一阵看下来,着实蛋疼. 当看了 skynet_mq. ...
- skynet源码阅读<3>--网关分析
继上一篇介绍了skynet的网络部分之后,这一篇以网关gate.lua为例,简单分析下其串接和处理流程. 在官方给出的范例中,是以examples/main.lua作为启动脚本的,在此过程中会创建wa ...
- skynet源码阅读<7>--死循环检测
在使用skynet开发时,你也许会碰到类似这样的警告:A message from [ :0100000f ] to [ :0100000a ] maybe in an endless loop (v ...
- skynet源码阅读<5>--协程调度模型
注:为方便理解,本文贴出的代码部分经过了缩减或展开,与实际skynet代码可能会有所出入. 作为一个skynet actor,在启动脚本被加载的过程中,总是要调用skynet.start和sky ...
- skynet源码阅读<6>--线程调度
相比于上节我们提到的协程调度,skynet的线程调度从逻辑流程上来看要简单很多.下面我们就来具体做一分析.首先自然是以skynet_start.c为入口: static void start(int ...
- skynet源码阅读<2>--网络部分
先来看下socket_server的数据结构,这里简称为ss: struct socket_server { int recvctrl_fd; int sendctrl_fd; int checkct ...
- skynet源码阅读<4>--定时器实现
昨天和三石公聊天,他提到timer的实现原理,我当时迟疑了一下,心想timer不是系统底层时钟中断驱动上层进程/线程,累积计时实现的么?他简述了timer的实现,什么堆排序,优先级队列等,与我想象的不 ...
- 用VC编译lua源码,生成lua语言的解释器和编译器
用VC编译lua源码,生成lua语言的解释器和编译器 1.去网址下载源码 http://www.lua.org/download.html 2.装一个VC++,我用的是VC6.0 3.接下来我们开始编 ...
- Redis源码阅读(一)事件机制
Redis源码阅读(一)事件机制 Redis作为一款NoSQL非关系内存数据库,具有很高的读写性能,且原生支持的数据类型丰富,被广泛的作为缓存.分布式数据库.消息队列等应用.此外Redis还有许多高可 ...
随机推荐
- [教程] 【终极开关机加速!!】手把手教你加速Mac的开关机速度。(经验证适用10.10!)
转自:http://bbs.feng.com/read-htm-tid-7811885.html [声明]如果锋友的机器开机速度已经很快了,譬如机械硬盘40秒左右,SSD10秒左右,那么就不要折腾 ...
- Codeforces 375 D Tree and Queries
Discription You have a rooted tree consisting of n vertices. Each vertex of the tree has some color. ...
- 问题:typedef char *pstring????
typedef char *pstring; const pstring cstr = 0; //cstr是指向char的常量指针: const pstring *ps; //ps是一个指针,它的对象 ...
- 【lombok】使用lombok注解,在代码编写过程中可以调用到get/set方法,但是在编译的时候无法通过,提示找不到get/set方法
错误如题:使用lombok注解,在代码编写过程中可以调用到get/set方法,但是在编译的时候无法通过,提示找不到get/set方法 报错如下: 解决方法: 1.首先查看你的lombok插件是否下载安 ...
- 解决asp.net core 日期格式 datetime Json返回 带T的问题
原文:解决asp.net core 日期格式 datetime Json返回 带T的问题 记录一下: Startup中,将 services.AddMvc(); 改为: services.AddMvc ...
- cart树剪枝
当前子树的损失函数: $C_a(T) = C(T) + a|T|$, 其中$C(T)$为对训练数据的预测误差,$|T|$为树的叶子结点数目,反映模型的复杂度.对固定的$a$,一定存在使损失函数$C_a ...
- Angularjs: call other scope which in iframe
Angularjs: call other scope which in iframe -------------------------------------------------------- ...
- BubbleGum96 开箱杂谈与软件资源
前言 原创文章,转载引用务必注明链接. 拿到有一段时间了,一直在想写哪些内容.96boards发布以来,吸引了很多眼球.这里我就慢慢随便聊聊,希望能让大家对96boards有更多了解. 开箱 [开箱图 ...
- weex 小结
1. import 文件时,必须引入全称,不能省略 .vue import mEcharts from '../components/Echarts.vue' 2.weex 的 cli 中没有 配置 ...
- 自己动手写CPU之第九阶段(4)——载入存储指令实现思路
将陆续上传新书<自己动手写CPU>,今天是第40篇,我尽量每周四篇,可是近期已经非常久没有实现这个目标了,一直都有事,不好意思哈. 开展晒书评送书活动,在q=%E4%BA%9A%E9%A9 ...