目标背景

在OpenResty/Nginx 中可以直接使用C/C++ 功能模块, HTTP请求服务器, 执行对应操作.

要完成该功能可以有两种方法:

  • 将其作为HTTP模块编写, 并编译进nginx 可执行文件(待补充)
  • 将C项目编译为动态库, 借助FFI, 调用API.

FFI 简介

FFI 库, 是LuaJIT 中的扩展库. 可实现从Lua 代码中调用外部C 函数, 使用C 数据结构.

JIT 编译器为Lua 代码直接访问C 数据结构和函数而产生的代码, 等同于一个C 编译器应该产生的代码. 在JIT编译过得代码中, 调用C 函数, 可以被内连处理, 不同于基于Lua/C API 函数调用.

LuaJIT 需要在编译时包含lua-nginx-module 模块.

C 标准库函数使用方法示例

-- ffi_c.lua
local ffi = require("ffi")

ffi.cdef[[
    int printf(const char *fmt, ...);
    int strcasecmp(const char *s1, const char *s2);
]]

ffi.C.printf("Hello %s.\n", "Lua")

local ret1 = ffi.C.strcasecmp("Hello", "hello")
print (ret1)
local ret2 = ffi.C.strcasecmp("Hello", "Lua")
print (ret2)
# 执行
luajit ffi_c.lua

C 自定义库使用方法示例

// ffi_c_add.c
#include <stdio.h>

int add(int x, int y){
    return x + y;
}
// 编译生成动态库
//gcc -g -o libfficadd.so -fpic -shared ffi_c_add.c
-- fficadd.lua
local ffi = require("ffi")
-- 此处如果使用 ffi.load("fficadd", true), 则接下里使用 ffi.C.add 调用
-- 此处此种写法的前提是引入了C 动态链接库 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:your_lib_path
-- 否则, 可以直接写 lib_full_path/libfficadd.so
local cadd = ffi.load("fficadd")

ff.cdef[[
    int add(int x, int y);
]]

local ret = cadd.add(1, 20)
print (ret)

-- luajit ffi_c_add.lua

Lua 处理cdata 对象

使用ffi.typeof 将C 类型转化为Lua ctype, 该函数返回一个ctype 变量类型.

local ffi = require("ffi")

ffi.cdef[[
    typedef struct {
        int a;
        int *b;
    }s1;

    struct s2 {
        int c;
        int *d;
    };
]]

print (ffi.typeof("int"))
print (ffi.typeof("s1"))
print (ffi.typeof("struct s2"))

创建并初始化cdata 对象

cdata = ffi.new(ct, [,nelem] [,init...])
cdata = ctype([nelem,] [init...])

FFI Sample

参考资料

  1. luajit ffi 小结