Skynet服务器框架(七) Lua中调用自定义C库

引言:

在skynet中,我们通常使用lua来写业务层的逻辑,并且每个功能模块基本上就是一个运行在沙盒中的lua服务。但是,当需要我们需要开发拓展的库或者进行高性能要求的模块开发时,还是需要考虑在C语言层面来开发一个动态库(.so),并提供可以在lua中调用的接口,然后再lua中调用此C库。

自定义C库:

查看了Lua官方的关于如何注册C库(C Libraries)的内容,其中有一段如下:

When you extend Lua with C functions, it is a good idea to design your code as a C library, even when you want to register only one C function: Sooner or later (usually sooner) you will need other functions. As usual, the auxiliary library offers a helper function for this job. The luaL_openlib function receives a list of C functions and their respective names and registers all of them inside a table with the library name.

大致的意思就是:当我们要使用c语言的功能函数来拓展Lua的功能时,将这些函数封装成一个C库是一个不错的选择。

1.动态库的标准:

根据官方的引导,定义一个C库的时候,在定义可以被调用的方法时与普通的C语言定义方式并无差别,只是需要额外添加一个数组和一个方法:

  • 定义一个测试函数:

    static int l_dir (lua_State *L) {
       ...  /* as before */
    }

  • 声明一个 luaL_reg 类型的数组:

    static const struct luaL_reg mylib [] = {
      {"dir", l_dir},
      {NULL, NULL}  /* sentinel */
    };

    这个数组的每个item都相当于一个key-value结构,包含两个属性 {"(lua调用时使用的函数名)",C定义函数名(函数指针)},这个数组就是用来指定此C库的各个函数以及他们在lua中被调用时对应使用的接口名称(注意:最后一个item必须是 {NULL, NULL},不可缺少)。

  • 定义一个 luaopen_* 函数,并调用 luaL_openlib 函数:

    int luaopen_mylib (lua_State *L) {
      luaL_openlib(L, "mylib", mylib, 0);
      return 1;
    }

    这个函数相当于作为此库的main函数。注意此函数的名称规则:

    • luaopen_是此函数的前缀,不可修改;
    • 后面的内容是我们在lua中使用require引用此库时的字符串名称(假如名称中带有“”,在使用require需要将“”替换为“.”,例如:“mylib_test”->“mylib.test”)。

    在Lua5.0中调用的是luaL_openlib,但是在Lua5.3中,则是使用luaL_newlib

    扫描二维码关注公众号,回复: 1496703 查看本文章

2.创建测试C库:

根据上述的定制标准,这里我们就来自定义一个自己C库,首先创建一个.c的文件,例如“mylib.c”,写几个测试方法:

    #include <lua.h>
    #include <lauxlib.h>
    #include <stdio.h>

    static int mtest1(lua_State *L){
        printf("--- mtest1\n"); 
        return 0;
    }

    static int mtest2(lua_State *L){
        //从传入参数table中获取第1个参数,转为整型
        int num = luaL_checkinteger(L,1);
        printf("--- mtest2:num=%d\n",num); 
        return 0;
    }

    int luaopen_mylib(lua_State *L){
        luaL_Reg l[] = {
            {"test1",mtest1},
            {"test2",mtest2},
            {NULL,NULL}
        };
        luaL_newlib(L,l);
        return 1;
    }

3.C库打包:

这里我使用在Linux环境下进行 C库的编译lua引用测试的执行 ,而且使用gnu make来作为构建工具,关于如何编写Makefile文件,这里就不做赘述了,做后端开发的话,特别是C++/C,还是应该学一下的。推荐教程:Linux下编写 makefile 详细教程

  • 创建Makefile:
    C语言文件类打包成动态库使用过Makefile来进行编译完成的,这里我们就根据我们创建的C源码文件来创建一个对应Makefile文件。

    CC ?= gcc  
    CFLAGS = -g -O2 -Wall -I$(LUA_INC)  
    SHARED := -fPIC --shared  
    
    TARGET = myLualib.so  
    LUA_CLIB_PATH = ./
    
    
    #引入lua头文件(根据你安装Lua库时的目录而定)
    
    LUA_INC ?= /usr/local/src/lua-5.3.0/src
    
    start: $(TARGET)  
    
    $(TARGET) : ./test.c
        $(CC) $(CFLAGS) $(SHARED) $^ -o $@  
    
    clean:  
        rm -fr $(TARGET)  
    
    $(LUA_CLIB_PATH) :  
        mkdir $(LUA_CLIB_PATH)

  • 执行打包指令:

    make
    • 执行输出:
    root@ubuntu:/application/tests# ls
    Makefile  test.c  test.lua
    root@ubuntu:/application/tests# make
    cc -g -Wall -I/usr/local/src/lua-5.3.0/src     -fPIC --shared   test.c -o mylib.so
    root@ubuntu:/application/tests# ls
    Makefile  mylib.so  test.c  test.lua
    • 也可以直接执行如下指令进行编译:
    gcc -g -Wall -I/usr/local/src/lua-5.3.0/src --shared -fPIC -o mylib.so ./test.c

执行编译的结果是我们能得到一个 mylib.so 动态库文件,那么接下来我们就要尝试在lua中引入此C库并调用其中的方法。

以上指令都需要在root权限下进行执行,假如当前不是root权限,需要输入 sudo su 进行切换。


lua中调用动态库接口:

新建一个测试的lua脚本,我这里取名为 test.lua

1.动态库引入:

--设置.so搜寻路劲
package.cpath = "./?.so" 
--加载我们自定义的库mylib.so
local mylib = require "mylib";

2.动态库调用:

--调用C库中的测试方法
mylib.test1()
mylib.test2(666)

3.运行测试脚本:

lua test.lua
  • 输出执行结果如下:
root@ubuntu:/application/tests# ls
Makefile  mylib.so  test.c  test.lua
root@ubuntu:/application/tests# lua test.lua
--- mtest1
--- mtest2:num=666


小结:

参考以上的步骤,以后我们就能够根据自己需要构建功能C库,然后导入到lua中进行使用,在使用skynet框架是尤为重要的技能。(发现了还有一个精简版的skynet,叫做hive


参考资料:

猜你喜欢

转载自blog.csdn.net/u010144805/article/details/80582694