skynet源码阅读<1>--lua与c的基本交互

2024-02-13 06:08

本文主要是介绍skynet源码阅读<1>--lua与c的基本交互,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

阅读skynet的lua-c交互部分代码时,可以看到如下处理:

 
  1. struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1));

  2.  

    那么,问题来了:skynet_context是如何作为upvalue与C函数绑定在一起的呢?这里以luaopen_skynet_core(lua_State *L)为例:

 
  1. int luaopen_skynet_core(lua_State *L) {

  2. luaL_checkversion(L);

  3. luaL_Reg l[] = { /*注册函数部分略去*/ { NULL, NULL } };

  4. luaL_newlibtable(L, l);

  5. lua_getfield(L, LUA_REGISTRYINDEX, "skynet_context");

  6. struct skynet_context *ctx = lua_touserdata(L,-1);

  7. if (ctx == NULL) {

  8. return luaL_error(L, "Init skynet context first");

  9. }

  10. luaL_setfuncs(L,l,1);

  11. return 1;

  12. }

  13.  

  这里先通过luaL_newlibtable创建一张表T(函数指针表l并未实际注册到表T中,只是分配了相应大小的空间),然后从全局索引表中取出事先注册的skynet_context,接着调用luaL_setfuncs(L,l,1),将函数表注册到T中。注册时每个函数都会从栈顶取出指定数目的元素(这里为1)作为upvalue。到lua源码中看看这部分具体的实现如何:

 
  1. /*

  2. ** set functions from list 'l' into table at top - 'nup'; each

  3. ** function gets the 'nup' elements at the top as upvalues.

  4. ** Returns with only the table at the stack.

  5. */

  6. LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {

  7. luaL_checkstack(L, nup, "too many upvalues");

  8. for (; l->name != NULL; l++) { /* fill the table with given functions */

  9. int i;

  10. for (i = 0; i < nup; i++) /* copy upvalues to the top */

  11. lua_pushvalue(L, -nup);

  12. lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */

  13. lua_setfield(L, -(nup + 2), l->name); /*-(nup+2)位置的元素正是之前创建的表T;这里以函数entry的name为key,向其注册闭包*/

  14. }

  15. lua_pop(L, nup); /* remove upvalues */

  16. }

  17.  

    可以看到扫描每个函数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):

 
  1. typedef struct CClosure {

  2. ClosureHeader;

  3. lua_CFunction f;

  4. TValue upvalue[1]; /* list of upvalues */

  5. } CClosure;

  6.  

    除了ClosureHeader之外(包括upvalue的个数,CommonHeader的对象类型tt_,标记mark_等),可以看到C闭包包含一个函数指针f和一个upvalue数组。upvalue数组的实际大小是根据upvalue的个数来创建的。这里要做的事情就是把upvalue拷进C闭包中,如下(lapi.c):

 
  1. LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {

  2. lua_lock(L);

  3. if (n == 0) {

  4. setfvalue(L->top, fn);

  5. }

  6. else {

  7. CClosure *cl;

  8. api_checknelems(L, n);

  9. api_check(L, n <= MAXUPVAL, "upvalue index too large");

  10. cl = luaF_newCclosure(L, n);

  11. cl->f = fn;

  12. L->top -= n;

  13. while (n--) {

  14. setobj2n(L, &cl->upvalue[n], L->top + n);

  15. /* does not need barrier because closure is white */

  16. }

  17. setclCvalue(L, L->top, cl);

  18. }

  19. api_incr_top(L);

  20. luaC_checkGC(L);

  21. lua_unlock(L);

  22. }

  23.  

    这里将栈上的元素copy到cl的upvalue数组中,然后再将cl设置到栈顶。之前被这里的while循环绕了一下,设n为N0,减一之后为N1,那么n--的运算结果为N0,但是循环中n的值则是N1,二者并不一样,逻辑是正确的。

    那么剩下的最后一个问题是,在C函数中,是如何取到相关的upvalue的?首先是通过lua_upvalueindex(1)拿到对应的upvalue的索引:

 
  1. #define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i))

  2.  

  在本例中,进入lua_touserdata,可以看到index2addr的实现:

 
  1. static TValue *index2addr (lua_State *L, int idx) {

  2. CallInfo *ci = L->ci;

  3. if (idx > 0) {

  4. TValue *o = ci->func + idx;

  5. api_check(L, idx <= ci->top - (ci->func + 1), "unacceptable index");

  6. if (o >= L->top) return NONVALIDVALUE;

  7. else return o;

  8. }

  9. else if (!ispseudo(idx)) { /* negative index */

  10. api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index");

  11. return L->top + idx;

  12. }

  13. else if (idx == LUA_REGISTRYINDEX)

  14. return &G(L)->l_registry;

  15. else { /* upvalues */

  16. idx = LUA_REGISTRYINDEX - idx;

  17. api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large");

  18. if (ttislcf(ci->func)) /* light C function? */

  19. return NONVALIDVALUE; /* it has no upvalues */

  20. else {

  21. CClosure *func = clCvalue(ci->func);

  22. return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : NONVALIDVALUE;

  23. }

  24. }

  25. }

  26.  

    在upvalue的处理这块,先是还原upvalue在C闭包中的index,然后从当前的CallInfo中取到闭包,拿到upvalue返回。

转载于:https://www.cnblogs.com/Jackie-Snow/p/5927890.html

这篇关于skynet源码阅读<1>--lua与c的基本交互的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/704680

相关文章

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Java使用ANTLR4对Lua脚本语法校验详解

《Java使用ANTLR4对Lua脚本语法校验详解》ANTLR是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件,下面就跟随小编一起看看Java如何使用ANTLR4对Lua脚本... 目录什么是ANTLR?第一个例子ANTLR4 的工作流程Lua脚本语法校验准备一个Lua Gramm

MySQL 中的 LIMIT 语句及基本用法

《MySQL中的LIMIT语句及基本用法》LIMIT语句用于限制查询返回的行数,常用于分页查询或取部分数据,提高查询效率,:本文主要介绍MySQL中的LIMIT语句,需要的朋友可以参考下... 目录mysql 中的 LIMIT 语句1. LIMIT 语法2. LIMIT 基本用法(1) 获取前 N 行数据(

Python Faker库基本用法详解

《PythonFaker库基本用法详解》Faker是一个非常强大的库,适用于生成各种类型的伪随机数据,可以帮助开发者在测试、数据生成、或其他需要随机数据的场景中提高效率,本文给大家介绍PythonF... 目录安装基本用法主要功能示例代码语言和地区生成多条假数据自定义字段小结Faker 是一个 python

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

基于Flask框架添加多个AI模型的API并进行交互

《基于Flask框架添加多个AI模型的API并进行交互》:本文主要介绍如何基于Flask框架开发AI模型API管理系统,允许用户添加、删除不同AI模型的API密钥,感兴趣的可以了解下... 目录1. 概述2. 后端代码说明2.1 依赖库导入2.2 应用初始化2.3 API 存储字典2.4 路由函数2.5 应

redis+lua实现分布式限流的示例

《redis+lua实现分布式限流的示例》本文主要介绍了redis+lua实现分布式限流的示例,可以实现复杂的限流逻辑,如滑动窗口限流,并且避免了多步操作导致的并发问题,具有一定的参考价值,感兴趣的可... 目录为什么使用Redis+Lua实现分布式限流使用ZSET也可以实现限流,为什么选择lua的方式实现

SpringBoot整合MybatisPlus的基本应用指南

《SpringBoot整合MybatisPlus的基本应用指南》MyBatis-Plus,简称MP,是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,下面小编就来和大家介绍一下... 目录一、MyBATisPlus简介二、SpringBoot整合MybatisPlus1、创建数据库和