lua调用c动态链接库

lua调用c动态链接库

Posted by 皮蛋啊皮蛋 on December 19, 2019

lua调用c动态链接库

主要方法

lua调用so文件主要两种方式:

  • C文件在提供编译后的so文件时,使用Lua扩展C库封装业务接口,使得接口和参数能够直接被lua识别,不需要额外的引用工具就可以直接调用接口,但是这种方式通过虚拟栈来传递Lua和C之间的调用参数和返回值,开发效率和通用性更低。

    参考链接:简单的lua扩展C库

  • 引入第三方依赖库直接调用传统的动态链接库,这样的好处在于不需要编写lua专用的so,更加具有通用性,性能也更高。第三方库目前用的比较多的有两种,一个是alien,一个是luajit的FFI。实际使用alien调用so文件时发现调用错误,查阅alien官方文档发现不支持嵌套的结构体类型,而FFI可以实现。

FFI的使用

    luajit是lua的一个Just-In-Time运行时编译器,也可以说是lua的一个高效版,而FFI直接集成在了luajit中所以不需要额外的安装步骤。luajit安装后续补充。

    FFI的引入和lua引用其他模块一样, ffi.load就是加载指定so文件,文件位置的指定可以直接绝对路径指定。

local ffi = require("ffi")
local example = ffi.load('./example.so')

    ffi.cdef是定义so文件中对应的需要调用的接口,如果涉及到结构体或者变量的定义,都放在这里,直接复制c的代码即可。

ffi.cdef[[
   typedef struct
    {
        char value[20];
        int num;
    } struct_a;

    typedef struct struct_b {
        uint32_t len;
        struct_a struct;
        char version[32];
        char text[0];
    } struct_b;
    
    char *decrypt(const char *text);
]]

    接口调用很简单,直接 example. decrypt(“abdce12345”) 就完成了接口的调用,但是实际出现问题在于lua中的变量类型(string)与c需要的变量类型(const char *)不一致,所以需要在调用前把变量类型转换正确。

    ffi.typeof的作用就是生成一个指定c语言类型的变量,在lua中统一显示为cdata类型,然后使用ffi.cast将数值复制到生产的变量中。对于结构体这样的复杂类型无法用cast进行赋值,使用ffi.typeof定义为结构体后使用ffi.new进行转换。ffi.new的作用就是新建一个指定类型的变量。

local char = ffi.typeof("const char*")
local res = ffi.cast(char, "12345abcde")
local data = {128,{"123456abcde",0} ,"",""}
local struct = ffi.typeof("struct struct_b")
local structData = ffi.new(struct, data)

    把上面涉及的内容连起来就完成了接口的基本调用。其中最需要注意的就是变量类型的对应和转换,比如 char * 类型的接口返回值是一个指针,在lua中并不能直接输出,需要通过ffi.string(pointer,128) 来获取地址所对应的多少长度的数据。

    实际调试时碰到最多的问题还是集中在地址的声明和使用上,最后还是把涉及地址的操作在so文件中进行,不在lua调用时创建地址空间。

更多FFI相关内容参考官方文档:luajit FFI

alien使用

    alien用起来流程跟FFI差不多,但是安装太过麻烦,也可能是用的服务器环境不太稳,服务器系统是centso7.3。

    alien官网推荐使用luarocks

LuaRocks is the package manager for Lua modules.

wget http://luarocks.org/releases/luarocks-*.tar.gz  
tar zxpf luarocks-*.tar.gz  
cd luarocks-* 
./configure
make bootstrap 

    按顺序执行即可,没啥大问题,下不下来就手动下,挑好版本。alien还有依赖需要安装,不知道干啥的下就完事了。

yum install -y libffi-devel
luarocks install alien

    下完了就用luarocks的命令安装alien,期间报错没有markdown命令,看别的博客说是python版本要升到2.7.13以上,然后开始升,一顿操作缺这个缺那个后放弃升级。后来想到办法单独装了个python_markdown,然后把markdown命令指向python_markdown后解决问题。

yum install python_markdown
su -c 'ln -s markdown_py markdown'

    贴一个简单例子

package.path = "/usr/local/share/lua/5.1/?.lua;"..package.path
package.cpath = "/usr/local/lib/lua/5.1/?.so;"..package.cpath

alien = require "alien"

libc = alien.load("./libmax.so")
libc.max:types("int","int","int")
res = libc.max(23,32)
print(res)

    看官方文档不支持嵌套的结构体,而实际需要用到嵌套的结构体所以没有研究,以后还要用到再补充。