linux下go的动态链接库的使用
转自:http://blog.csdn.net/xtxy/article/details/21328143
在使用lua进行服务器端游戏逻辑开发时,发现了LUA的各种不方便的地方,不能编译检查,不能断点调试,笔误的函数和变量不提示出错等等,所以有了全部使用go来做服务器端开发的想法。
如果不需要热更新,那使用go开发服务器逻辑是很轻松的,而游戏服务器特别是页游,一般都需要支持热更新,所以我决定使用go的动态链接库方式来实现,也就是底层框架是go,上层逻辑是go的动态链接库。go原生不支持动态链接库,在查阅了很多文章之后,决定使用gccgo来实现。
经过了大约一周的时间,终于把框架搭建起来了,期间遇到了一些比较坑的问题,记录在此,以便以后不会再犯,也可以帮助其他有同样需求的同学快速搭建这样的框架。
这个例子需要了解go目录构建和环境变量的知识,如果不了解,可以先看看网上的文章,很简单的。
整个框架搭建好了之后的方式是GOCCGO,就是GO -> C -- dll --> C -> GO。底层和最上层都是GO,中间使用了C来提供动态链接库的方式。
1 首先来看底层特殊的地方,就是GO->C的部分。
go文件如下:
// script.go package script // 1
//extern initDll
func c_initDll(string, string) //extern runDll
func c_runDll(string, map[string]interface{}) string var dataMap map[string]interface{} // func Init(fileName string, funcName string) {
dataMap = make(map[string]interface{}) return c_initDll(fileName, funcName)
} func Run(buf []byte) string {
str := string(buf) retStr := c_runDll(str, dataMap) return retStr
}
其中1处是gccgo的特殊写法,//extern funcName就是说有一个c的函数,在go中使用c_funcName来调用它。(是不是能使用其他名字我没有试过)
2处是声明了一个map结构,这个后面再说,此时先不管。
文件中的Init函数在程序启动时初始化时调用,用来加载dll;Run函数就是调用dll来处理每次的游戏逻辑了。
对应的c文件如下:
// script.c #include <dlfcn.h> struct __go_string { //
const unsigned char *__data;
int __length;
}; struct __go_string (*dllEntry)(struct __go_string, int); // void loadDll(struct __go_string fileName, struct __go_string funcName)
{
void *dll = dlopen(fileName.__data, RTLD_LAZY); //
if(!dll)
{
// error
} dlerror(); *(void **)(&dllEntry) = dlsym(dll, funcName.__data); //
if(NULL != dlerror())
{
// error
}
} struct __go_string runDll(struct __go_string inData, void *dataMap)
{
return dllEntry(inData, dataMap); //
}
其中1处是go中string类型在c中的表示,string在c中是一个结构体。
2处是动态链接库的主函数,动态链接库导出这个函数来对游戏逻辑进行处理。
3处和4处分别是加载动态链接库和获取函数地址的代码。
5处就是调用动态链接库的主函数的地方,例子中传递参数为string和一个指针(其实就是Map的地址),返回值也是一个string。
编译他们的方法如下:
gccgo -o testc.o -c test.c
gccgo -o testgo.o -c test.go
ar cr libtest.a testgo.o testc.o
最后编译出来的libtest.a,需要放到pkg目录下面相应的地方。也就是你使用go install命令时它将生成出来的库放到哪里,你就讲libtest.a拷贝到哪里。
GO->C的部分就已经完成了。
2 动态链接库的接口, C->GO的部分
c文件如下:
// dll.c struct __go_string {
const unsigned char *__data;
int __length;
}; extern struct __go_string go_entry(struct __go_string, int, void *) \
__asm__ ("GameLogic_ctrl.Entry"); // 1 // void __attach(void) __attribute__ ((constructor)); // struct __go_string centry(struct __go_string input, void *dataMap)
{
return go_entry(input, dataMap);
}
1处是C调用GO函数的声明,Entry是函数名称,GamLogic_ctrl是包名称,如果这个地方出错的话,你可以用nm命令查看go生成的lib文件,将包名称修改正确。
2是动态链接库的初始化函数声明,如果需要,就可以添加一个,调用go函数,做一些初始化操作。
对应的go文件如下:
// dllMain.go package ctrl func Entry(inData string, index int, dataMap map[string]interface{}) string {
return ""
}
和普通的go文件写法是一样的。
编译他们的方法如下:
go install -compiler=gccgo -gccgoflags='-fPIC' // 将所有的go文件编译出来
gccgo -o libcdll.a -c dll.c -fPIC // 将dll入口的c文件编译出来
然后将libcdll.a拷贝到其他go生成的库的目录下,执行:
gccgo -shared -o dllMain.so *.a // 将单个库文件编译成最终的动态链接库
整个框架动态链接库部分就完成了。
下面我说一下使用这个框架需要注意的地方:
1. 编译动态链接库时除了c部分外,其他不要直接使用gccgo命令,因为一些外部库,比如访问mongodb的mgo库,你是无法使用gccgo编译出来的(或者说很困难),最简单的方法就是用go install -compiler=gccgo的方式;
2. 动态链接库不会执行初始化部分,也就是说在package里面的init函数并不会被调用;
3. 和上面一条比较像,import的外部库(包括系统库),比如fmt并不会被import进来。解决方法是在基础框架部分(也就是可执行文件),将动态链接库需要用到的库全部import进来;
4. 动态链接库里面申请的内存在调用完成后会被释放。比如在动态链接库主函数文件(上面最后一个包含Entry函数的文件)里面加一个包里面的全局变量:var tmpMap = make(map[string]interface{}),如果在Entry函数里面访问tmpMap,你会发现tmpMap指向的map空间已经被释放了,访问它会报段错误。解决方法是在基础框架里声明一个map结构,然后传递给动态链接库使用,这个map数据就不会被释放。也就是在我列举的第一个文件(script.go)的注释2处,声明了一个dataMap,就是起这个作用。
5. 在gccgo的说明文档里,slice是可以在c和go之间作为函数参数传递的,但是我试过了之后,发现有问题。首先是go的slice到c里面之后,并不是一个struct,而是一个struct *,就是说是一个指针,而且我将指针指向的内存打印出来,和文档上说的完全对不上,找不到字符数组,找不到len的位置,cap的位置似乎在这个指针向上偏移4个字节……我被搞蒙了,就没有研究了,用string来代替slice进行传输。
由于是自己摸索出来的东西,所以可能有一些地方不正确,希望大家指正,我随时修改。
linux下go的动态链接库的使用的更多相关文章
- Linux下c++通过动态链接库调用类
http://hi.baidu.com/ablenavy/item/b498901c6826bbf587ad4e33 Linux下的动态链接库叫so,即Shared Object,共享对象.一些函数就 ...
- Linux下gcc编译生成动态链接库*.so文件并调用它【转载】
动态库*.so在linux下用c和c++编程时经常会碰到,最近在网站找了几篇文章介绍动态库的编译和链接,总算搞懂了这个之前一直不太了解得东东,这里做个笔记,也为其它正为动态库链接库而苦恼的兄弟们提供一 ...
- Linux下gcc编译生成动态链接库*.so文件并调用它 是转载的
动态库*.so在linux下用c和c++编程时经常会碰到,最近在网站找了几篇文章介绍动态库的编译和链接,总算搞懂了这个之前一直不太了解得东东,这里做个笔记,也为其它正为动态库链接库而苦恼的兄弟们提供一 ...
- Linux下gcc编译生成动态链接库*.so文件并调用它
动态库*.so在linux下用c和c++编程时经常会碰到,最近在网站找了几篇文章介绍动态库的编译和链接,总算搞懂了这个之前一直不太了解得东东,这里做个笔记,也为其它正为动态库链接库而苦恼的兄弟们提供一 ...
- Linux下gcc编译生成动态链接库*.so文件并调用它(注:执行Test程序后无需用export 命令指定.so库文件路径:方法在文中下方;)
动态库*.so在linux下用c和c++编程时经常会碰到,最近在网站找了几篇文章介绍动态库的编译和链接,总算搞懂了这个之前一直不太了解得东东,这里做个笔记,也为其它正为动态库链接库而苦恼的兄弟们提供一 ...
- .netcore在linux下使用P/invoke方式调用linux动态库
http://www.mamicode.com/info-detail-2358309.html .netcore下已经实现了通过p/invoke方式调用linux的动态链接库(*.so)文件 1 ...
- linux下动态链接库解决方案(二)
以前写过一个关于linux下用c++写动态链接库无法通过的解决方案,今天看到<linux C程序设计-王者归来>这本书,书中有个更容易的解决方案,特此记录下来 书中使用的是c语言,我改用c ...
- linux下动态链接库解决方案(一)
1.c++无法直接调用用c写的动态链接库,如果调用的话可能需要用到一些交叉编译的知识: 2.在c++写的动态链接库无法被调用成功的反思: 在linux下,通常都是使用g++编译器("g++ ...
- linux下动态链接库.so文件 静态链接库.a文件创建及使用
转摘网址为:http://www.cnblogs.com/fengyv/archive/2012/08/10/2631313.html Linux下文件的类型是不依赖于其后缀名的,但一般来讲: ...
随机推荐
- Webpack探索【2】--- 安装、项目初始化、webpack.config.js配置文件
本文主要讲安装.项目初始化.webpack.config.js配置文件方面的内容.
- 我的Android进阶之旅------>Android无第三方Jar包的源代报错:The current class path entry belongs to container ...的解决方法
今天使用第三方Jar包afinal.jar时候,想看一下源代码,无法看 然后像添加jar对应的源码包,也无法添加相应的源代码,报错如下:The current class path entry bel ...
- Android 主线程和子线程通信问题
Android 如今不支持View在子线程中创建及调用其方法.假设要实现子线程内容更新之后.将结果及时反馈到主线程中,该怎样出来呢? 能够在主线程中创建Handler来实现. 这样子线 ...
- easyui的 一些经验
1. 渲染网络表格时,行操作 <th field="sort_num" width="10" data-options="field:'id', ...
- SVG嵌入HTML
将SVG图像嵌入到HTML文件有多种方法: 使用<iframe>元素来嵌入SVG图像 使用<img>元素来嵌入SVG图像 将SVG图像作为背景图像嵌入 直接使用<svg& ...
- LeetCode:位运算实现加法
LeetCode:位运算实现加法 写在前面 位运算符 实现加法的思路 两个加数,比如5(101)和6(110),如何不用加法就能得出两者之和呢? 我们知道二进制计算中,如果使用异或将会产生无进位的两者 ...
- flask初次搭建rest服务笔记
官网中有用的记录一下,太多只是记录了最简单的官网docs:http://flask.pocoo.org/docs/0.12/ 跑起来一个程序 $ export FLASK_APP=hello.py $ ...
- 深入理解利用new创建对象的执行过程以Person p=new Person("张三",20);为例
代码如下: class Person { private String name="haha"; private int age; private static String co ...
- RabbitMQ高级应用
高级应用一: 手动模式和自动应答模式 1. 了确保消息不会丢失,RabbitMQ支持消息应答.消费者发送一个消息应答,告诉RabbitMQ这个消息已经接收并且处理完毕了.RabbitMQ就可以删除它了 ...
- 使用 sqoop 将mysql数据导入到hdfs(import)
Sqoop 将mysql 数据导入到hdfs(import) 1.创建mysql表 CREATE TABLE `sqoop_test` ( `id` ) DEFAULT NULL, `name` va ...