其他分享
首页 > 其他分享> > lua和go混合调用调试记录支持跨平台(通过C和LuaJit进行实现)

lua和go混合调用调试记录支持跨平台(通过C和LuaJit进行实现)

作者:互联网

最近在做物联网平台的协议开发,但是因为前端设备厂家较多,而且根据使用的场景和使用的用途,协议也大小不一,各种各样的协议都有,但是为了来兼容这些协议,必须要设计一些插件或者脚本来自动进行数据的解析和数据的封装。然后调查了一下,市面上使用较为广泛的是lua和js,这两个脚本型语言使用的人生较多,用起来也比较容易上手。

然后在对脚本支持上就麻烦了,毕竟没有没有那么多的时间来做这方面的工作。然后在网站找了相关资料,还是决定从LuaJit入手,毕竟之前一直在做嵌入式的开发,对C的移植和使用也比较熟悉,然后使用LuaJit运行的时间也比较款,因此决定,还是从LuaJit下手。

我们简单的来看看LuaJit的相关知识和优缺点。

一、LuaJIT主要由以下四部分组成:

语法实现。
Trace JIT编译器。
库函数。
原生库++(强化过的原生库)
bit
ffi
jit
字节码。
注:最新luajit对应lua5.1.5。

二、为什么要使用LuaJit
解释执行:

效率低。
代码暴露。
静态编译:

不够灵活,无法热更新。
平台兼容性差。
JIT:

效率:高于解释执行,低于静态编译。
安全性:一般都会先转换成字节码。
热更新:无论源码还是字节码本质上都是资源文件。
兼容性:虚拟机会处理平台差异,对用户透明。

 

 

三、GO和Lua通信的桥梁---->C语言

为了快速打通Go和Lua的使用途径,决定举出大宝剑C语言,毕竟C属于较为底层的语言,各个语言为了实现某些高效率的场景,都有支持C语言,因此决定,从C语言入手,以C语言为桥梁,沟通Go语言和Lua语言,接下来我们看如何在Go语言中使用C语言。

1、启用 CGO 特性

在 golang 代码中加入 import “C” 语句就可以启动 CGO 特性。这样在进行 go build 命令时,就会在编译和连接阶段启动 gcc 编译器。

 

// go.1.15// test.go
package main
import "C"      // import "C"更像是一个关键字,CGO工具在预处理时会删掉这一行
 
func main() {
}

  

说明:

当你在包中引用 import "C",go build 就会做很多额外的工作来构建你的代码,构建就不仅仅是向 go tool compile 传递一堆 .go 文件了,而是要先进行以下步骤:

1)cgo 工具就会被调用,在 C 转换 Go、Go 转换 C 的之间生成各种文件。

2)系统的 C 编译器会被调用来处理包中所有的 C 文件。

3)所有独立的编译单元会被组合到一个 .o 文件。

4)生成的 .o 文件会在系统的连接器中对它的引用进行一次检查修复。

cgo 是一个 Go 语言自带的特殊工具,可以使用命令 go tool cgo 来运行。它可以生成能够调用 C 语言代码的 Go 语言源文件,也就是说所有启用了 CGO 特性的 Go 代码,都会首先经过 cgo 的"预处理"。

2.Go 调用自定义 C 程序

// test_call_c.go
package main
 
/*
#cgo LDFLAGS: -L/usr/local/lib
#include <stdio.h>
#include <stdlib.h>
#define REPEAT_LIMIT 3              // CGO会保留C代码块中的宏定义
typedef struct{                     // 自定义结构体
    int repeat_time;
    char* str;
}blob;
int SayHello(blob* pblob) {  // 自定义函数
    for ( ;pblob->repeat_time < REPEAT_LIMIT; pblob->repeat_time++){
        puts(pblob->str);
    }
    return 0;
}
*/
import "C"
import (
    "fmt"
    "unsafe"
)
 
func main() {
    cblob := C.blob{}                               // 在GO程序中创建的C对象,存储在Go的内存空间
    cblob.repeat_time = 0
 
    cblob.str = C.CString("Hello, World\n")         // C.CString 会在C的内存空间申请一个C语言字符串对象,再将Go字符串拷贝到C字符串
 
    ret := C.SayHello(&cblob)                       // &cblob 取C语言对象cblob的地址
 
    fmt.Println("ret", ret)
    fmt.Println("repeat_time", cblob.repeat_time)
 
    C.free(unsafe.Pointer(cblob.str))               // C.CString 申请的C空间内存不会自动释放,需要显示调用C中的free释放
}

  

CGO 会保留序文中的宏定义,但是并不会保留注释,也不支持#program,C 代码块中的#program 语句极可能产生未知错误。

CGO 中使用 #cgo 关键字可以设置编译阶段和链接阶段的相关参数,可以使用 ${SRCDIR} 来表示 Go 包当前目录的绝对路径。

使用 C.结构名 或 C.struct_结构名 可以在 Go 代码段中定义 C 对象,并通过成员名访问结构体成员。

test3.go 中使用 C.CString 将 Go 字符串对象转化为 C 字符串对象,并将其传入 C 程序空间进行使用,由于 C 的内存空间不受 Go 的 GC 管理,因此需要显示的调用 C 语言的 free 来进行回收。

四.编译LuaJit库文件

1. 安装 TDM-GCC。

下载并安装 TDM-GCC 编译器 :

下载地址 : tdm-gcc

 

 

 

 

 

 

选择支持32位和64位的版本版本 

 

 

设置安装路径

 

 

全部选择

 

 

然后等待安装完成,安装完成之后,配置应用程序的路径:

 

 

编译源代码并生成库文件

mingw32-make

  

 

 

 

 

五、go调用lua支持

 
#include <luajit.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdio.h>
#include <stdlib.h>
#include "_cgo_export.h"
 
extern int sync_extern_method(lua_State* _L);
 
int gluaL_dostring(lua_State* _L, char* script) {
	int res = luaL_dostring(_L, script);
	free(script);
	return res;
}
void glua_getglobal(lua_State* _L, char* name) {
	lua_getglobal(_L, name);
	free(name);
}
void glua_setglobal(lua_State* _L, char* name) {
	lua_setglobal(_L, name);
	free(name);
}
void glua_pushlightuserdata(lua_State* _L, void* obj) {
	lua_pushlightuserdata(_L, obj);
}
int glua_pcall(lua_State* _L, int args, int results) {
	return lua_pcall(_L, args, results, 0);
}
lua_Number glua_tonumber(lua_State* _L, int index) {
	return lua_tonumber(_L, index);
}
int glua_yield(lua_State *_L, int nresults) {
	return lua_yield(_L, nresults);
}
const char* glua_tostring(lua_State* _L, int index) {
	return lua_tostring(_L, index);
}
void glua_pop(lua_State* _L, int num) {
	lua_pop(_L, num);
}
lua_State *glua_tothread(lua_State* _L, int index) {
	return lua_tothread(_L, index);
}
 
int glua_istable(lua_State* _L, int index) {
	return lua_istable(_L, index);
}
void* glua_touserdata(lua_State* _L, int index) {
	return lua_touserdata(_L, index);
}
 
int glua_resume (lua_State *_L, int narg) {
	return lua_resume(_L, narg);
}
 
int glua_gettop(lua_State *_L) {
	return lua_gettop(_L);
}
 
int glua_gc (lua_State *_L, int what, int data) {
	return lua_gc(_L, what, data);
}
 
lua_State *gluaL_newstate (void) {
	return luaL_newstate();
}
 
void gluaL_openlibs (lua_State *_L) {
	luaL_openlibs(_L);
}
 
lua_State *glua_newthread (lua_State *_L) {
	return lua_newthread(_L);
}
 
void glua_close (lua_State *_L) {
	lua_close(_L);
}
 
void glua_remove (lua_State *_L, int index) {
	lua_remove(_L, index);
}
 
int glua_type (lua_State *_L, int index) {
	return lua_type(_L, index);
}
 
void glua_pushlstring (lua_State *_L, char *s, size_t len) {
	lua_pushlstring (_L, s, len);
	free(s);
}
 
void glua_pushnumber (lua_State *_L, lua_Number n) {
	lua_pushnumber(_L, n);
}
 
void glua_pushboolean (lua_State *_L, int b) {
	lua_pushboolean(_L, b);
}
 
void glua_pushnil (lua_State *_L) {
	lua_pushnil(_L);
}
 
void glua_createtable (lua_State *_L, int narr, int nrec) {
	lua_createtable(_L, narr, nrec);
}
 
void glua_settable (lua_State *_L, int index) {
	lua_settable (_L, index);
}
 
int glua_next (lua_State *_L, int index) {
	return lua_next(_L, index);
}
 
int glua_toboolean (lua_State *_L, int index) {
	return lua_toboolean(_L, index);
}
 
void register_go_method(lua_State* _L) {
	lua_pushcfunction(_L, &sync_extern_method);
	lua_setglobal(_L, "sync_extern_method");
}

  添加跨平台支持

.

 

 

六 、编写测试文件

1.go语言文件内容如下:

package main
 
import (
	"context"
	"encoding/json"
	"fmt"
	"time"
 
	"github.com/xiaodingding/iotfast/library/libLua"
)
 
func test_sum(ctx context.Context, args ...interface{}) (interface{}, error) {
	sum := 0
	for _, arg := range args {
		sum = sum + int(arg.(int64))
	}
	if sum%2 == 0 {
		return sum, nil
	} else {
		return nil, fmt.Errorf("bad sum")
	}
}
 
func json_decode(ctx context.Context, args ...interface{}) (interface{}, error) {
	raw := args[0].(string)
 
	var res map[string]interface{}
	err := json.Unmarshal([]byte(raw), &res)
	return res, err
}
 
func main() {
 
	fmt.Println("start main")
 
	libLua.RegisterExternMethod("json_decode", json_decode)
	libLua.RegisterExternMethod("test_sum", test_sum)
 
	s := time.Now()
 
	fmt.Println("time:", s)
 
	res, err := libLua.NewAction().WithScript(`
	function fib(n)
		if n == 0 then
			return 0
		elseif n == 1 then
			return 1
		end
		return fib(n-1) + fib(n-2)
	end
	`).WithEntrypoint("fib").AddParam(35).Execute(context.Background())
	fmt.Println(time.Now().Sub(s))
	fmt.Println(res, err)
	s = time.Now()
	res, err = libLua.NewAction().WithScriptPath("./script.lua").WithEntrypoint("fib").AddParam(35).Execute(context.Background())
	fmt.Println(time.Now().Sub(s))
	fmt.Println(res, err)
	s = time.Now()
	res, err = libLua.NewAction().WithScriptPath("./script.lua").WithEntrypoint("fibt").AddParam(35).Execute(context.Background())
	fmt.Println(time.Now().Sub(s))
	fmt.Println(res, err)
	s = time.Now()
	res, err = libLua.NewAction().WithScriptPath("./script.lua").WithEntrypoint("test_args").AddParam([]interface{}{69, 56}).Execute(context.Background())
	fmt.Println(time.Now().Sub(s))
	fmt.Println(res, err)
	s = time.Now()
	res, err = libLua.NewAction().WithScriptPath("./script.lua").WithEntrypoint("async_json_encode").Execute(context.Background())
	fmt.Println(time.Now().Sub(s))
	fmt.Println(res, err)
	s = time.Now()
	res, err = libLua.NewAction().WithScriptPath("./script.lua").WithEntrypoint("test_pull_table").AddParam(69).Execute(context.Background())
	fmt.Println(time.Now().Sub(s))
	fmt.Println(res, err)
	fmt.Println("end main")
}

  2.lua语言内容如下:

 
function fib(n)
    if n == 0 then
        return 0
    elseif n == 1 then
        return 1
    end
    return fib(n-1) + fib(n-2)
end
 
function fibt(n)
    return fibc(n, 0, 1)
end
 
function fibc(n, a, b)
    if n == 0 then
        return a
    else
        if n == 1 then return b end
    end
    return fibc(n-1, b, a+b)
end
 
function test_args(n)
    res, err = sync_extern_method('test_sum', 1,2,3,4,5,6,n[1],n[2])
    if err == nil then
        return res
    else
        error(err)
    end
end
 
function test_pull_table(obj)
    return {a=true, b=123, c='hello luajit', d={e=12, f='good golang'}, e={1,2,3,4,4}, 1, m=obj}, nil
end
 
function async_json_encode()
    return coroutine.yield('json_decode', '{"a":"ads","b":12,"c":"sadh"}', 'hello world')
end

  3.文件夹内容结构

 

 

4.windows下运行测试

 

 

Windos测试通过。

 

5.Linux下运行测试类似于Windows。

TRANSLATE with x English
Arabic Hebrew Polish
Bulgarian Hindi Portuguese
Catalan Hmong Daw Romanian
Chinese Simplified Hungarian Russian
Chinese Traditional Indonesian Slovak
Czech Italian Slovenian
Danish Japanese Spanish
Dutch Klingon Swedish
English Korean Thai
Estonian Latvian Turkish
Finnish Lithuanian Ukrainian
French Malay Urdu
German Maltese Vietnamese
Greek Norwegian Welsh
Haitian Creole Persian  
  TRANSLATE with COPY THE URL BELOW Back EMBED THE SNIPPET BELOW IN YOUR SITE Enable collaborative features and customize widget: Bing Webmaster Portal Back

标签:return,LuaJit,int,fmt,State,lua,跨平台,glua
来源: https://www.cnblogs.com/kmust/p/16492143.html