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

相关文章

Python中多线程和多进程的基本用法详解

《Python中多线程和多进程的基本用法详解》这篇文章介绍了Python中多线程和多进程的相关知识,包括并发编程的优势,多线程和多进程的概念、适用场景、示例代码,线程池和进程池的使用,以及如何选择合适... 目录引言一、并发编程的主要优势二、python的多线程(Threading)1. 什么是多线程?2.

MyBatis-Flex BaseMapper的接口基本用法小结

《MyBatis-FlexBaseMapper的接口基本用法小结》本文主要介绍了MyBatis-FlexBaseMapper的接口基本用法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具... 目录MyBATis-Flex简单介绍特性基础方法INSERT① insert② insertSelec

JAVA调用Deepseek的api完成基本对话简单代码示例

《JAVA调用Deepseek的api完成基本对话简单代码示例》:本文主要介绍JAVA调用Deepseek的api完成基本对话的相关资料,文中详细讲解了如何获取DeepSeekAPI密钥、添加H... 获取API密钥首先,从DeepSeek平台获取API密钥,用于身份验证。添加HTTP客户端依赖使用Jav

C++中使用vector存储并遍历数据的基本步骤

《C++中使用vector存储并遍历数据的基本步骤》C++标准模板库(STL)提供了多种容器类型,包括顺序容器、关联容器、无序关联容器和容器适配器,每种容器都有其特定的用途和特性,:本文主要介绍C... 目录(1)容器及简要描述‌php顺序容器‌‌关联容器‌‌无序关联容器‌(基于哈希表):‌容器适配器‌:(

Go中sync.Once源码的深度讲解

《Go中sync.Once源码的深度讲解》sync.Once是Go语言标准库中的一个同步原语,用于确保某个操作只执行一次,本文将从源码出发为大家详细介绍一下sync.Once的具体使用,x希望对大家有... 目录概念简单示例源码解读总结概念sync.Once是Go语言标准库中的一个同步原语,用于确保某个操

使用Python进行文件读写操作的基本方法

《使用Python进行文件读写操作的基本方法》今天的内容来介绍Python中进行文件读写操作的方法,这在学习Python时是必不可少的技术点,希望可以帮助到正在学习python的小伙伴,以下是Pyth... 目录一、文件读取:二、文件写入:三、文件追加:四、文件读写的二进制模式:五、使用 json 模块读写

基于WinForm+Halcon实现图像缩放与交互功能

《基于WinForm+Halcon实现图像缩放与交互功能》本文主要讲述在WinForm中结合Halcon实现图像缩放、平移及实时显示灰度值等交互功能,包括初始化窗口的不同方式,以及通过特定事件添加相应... 目录前言初始化窗口添加图像缩放功能添加图像平移功能添加实时显示灰度值功能示例代码总结最后前言本文将

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

基本知识点

1、c++的输入加上ios::sync_with_stdio(false);  等价于 c的输入,读取速度会加快(但是在字符串的题里面和容易出现问题) 2、lower_bound()和upper_bound() iterator lower_bound( const key_type &key ): 返回一个迭代器,指向键值>= key的第一个元素。 iterator upper_bou

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听