阅读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返回。

最新文章

  1. awk 例子
  2. pycurl
  3. 【C#学习笔记】写文件
  4. java web第一个Servlet程序
  5. dp 斯特林数 HDU2512一卡通大冒险
  6. 开始MVC5之旅
  7. Python使用shape计算矩阵的行和列
  8. RecyclerView学习笔记
  9. Spring+SpringMVC+MyBatis+easyUI整合基础篇(八)mysql中文查询bug修复
  10. 三 Django模型层之Meta
  11. Python里面 search0和 match0的区别?
  12. jQuery实现单击某个标签改变样式
  13. feed
  14. word2vec原理推导与代码分析
  15. Ubuntu16.4下QT配置opencv3.1+FFmpeg
  16. P2889 [USACO07NOV]挤奶的时间Milking Time
  17. CSS------让ul中高度不同的li底部对齐
  18. Android适配底部返回键等虚拟键盘的完美解决方案
  19. 环境变量PATH/cp命令/mv命令/文档查看cat/more/less/head/tail
  20. var a =10 与 a = 10的区别

热门文章

  1. spring boot 添加mybatis,以及相关配置
  2. Flux --&gt; Redux --&gt; Redux React 入门 基础实例使用
  3. Linux上利用NFS实现远程挂载
  4. 使用Python写的第一个网络爬虫程序
  5. 出现异常时直接把e输出比输出e.getMessage()好得多
  6. reorder-list——链表、快慢指针、逆转链表、链表合并
  7. Effective C++ 条款七 为多态基类声明virtual析构函数
  8. HTML5面试题-b
  9. break 用法
  10. iGrimaceVX3.0和1.44在线源手机直接安装教程