您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
分享到
使用c++编写和使用.so动态链接库
 
火龙果软件    发布于 2014-10-21
  2934  次浏览      19
 

1,使用 c 生成动态链接库mylib.so的简单示例

声明文件mylib.h

#ifndef __MY_LIB_H__
#define __MY_LIB_H__
void foobar(int i);
#endif /* __MY_LIB_H__ */

实现文件mylib.c

#include <stdio.h>
#include "mylib.h"
void foobar(int i)
{
printf("Printing from mylib.so %d\n", i);
}

使用 gcc 将 mylib.c 编译成一个共享对象文件,如下命令:

[steven@sasd c++]$ gcc-fPIC -shared -o mylib.so mylib.c
  [steven@sasd c++]$ ll
  total 16
  -rw-rw-r--. 1 steven steven 109 Apr 13 09:27 mylib.c
  -rw-rw-r--. 1 steven steven 90 Apr 13 09:26 mylib.h
  -rwxrwxr-x. 1 steven steven 6210 Apr 1309:28 mylib.so

注:

2这里的共享对象其实全称是动态共享对象文件(Dynamic Shared Objects,简写为DSO);

2-fPIC:地址无关代码(Position-Independent Code),该技术主要用于解决SO中对绝对地址的重定位问题;

2,动态链接.so

下面在我们的c++程序中链接并使用此.so文件,在C++中,是使用 linkage directive 方式指出任意非C++函数所用的语言,也就是用extern “C”来指示对应的声明是使用C来实现的,这样对于该声明中的文件的编译会采用对应的C编译器来编译处理,如下为使用示例:

// use_mylib.cpp
#include <iostream>
#include <string>
using namespace std;
#ifdef __cplusplus
extern "C" {
#endif
#include "mylib.h" // for foobar()
#ifdef __cplusplus
}
#endif
int main()
{
foobar(100);
return 0;
}

编译及运行结果:

[steven@sasd c++]$ g++ -o use_mylibuse_mylib.cpp ./mylib.so
  [steven@sasd c++]$ ./use_mylib
  Printing from mylib.so 100
  [steven@sasd c++]$

3,运行时动态加载.so

动态库的运行时加载时通过一系列由动态链接器(dynamic linker)提供的API来实现的,这几个API的实现都是在libdl.so中的,且相关的声明和常量定义在头文件<dlfcn.h>中。

dlopen():主要用来打开一个.so,并将其加载到进程的地址空间,完成初始化过程,原型如下:

void *dlopen(const char *filename, int flag);

filename:the dynamic library file named by the null-terminated string;对于路径设置为相对路径或绝对路径情形下,dlopen()是如何查找该动态库文件的,这个直接参考man;

flag:用于表示函数符号的解析方式,如:RTLD_LAZY表示使用延迟绑定,只有在函数第一次被使用时才会去绑定,即PLT机制;相对应的为RTLD_NOW表示当模块被加载时即完成所有的函数绑定工作,如果有任何未定义的符号引用的绑定工作无法完成,dlopen都直接返回错误;其他的flag标识可以直接参考man;

返回值是被加载的模块的句柄,该句柄会被其他的几个API使用到,如果加载失败,则返回NULL。

dlsym():通过此函数来找到所需要的符号,定义如下:

void *dlsym(void *handle, const char  *symbol);

第一个参数handle就是dlopen返回的句柄,第二个参数symbol表示要查找的符号的名字,一个以‘\0’ 结尾的字符串;

如果dlsym找到了相应的符号,则返回该符号的值;如果没有找到,则返回NULL。

对于不同类型的符号,返回的指针意义不同:如果为函数,则为函数地址;如果为变量,则为变量的地址;如果为常量,则应该为常量的值;

dlerror():在调用dlopen,dlsym,dlclose之后,都可以通过dlerror()来判断上一次调用是否成功,dlerror的返回值为char*,如果返回NULL,则表示调用成功;否则返回对应的错误消息;函数原型声明如下:

char *dlerror(void);

dlclose():卸载一个已经加载的模块。

在系统内部会维持一个加载引用计数器,每次调用dlopen()来加载模块时,相应的计数器加1;每次调用dlclose()卸载某模块时,相应计数器减1。当计数器等于0时,该模块才会真正被卸载掉。

下面直接上示例:

// ex_dload_so.cpp
#include <iostream>
using namespace std;
#include <dlfcn.h>
int main()
{
// 为上面生成的.so文件的绝对路径
const char *MYLIB_PATH = "/home/steven/test/c++/mylib.so";
// 加载指定的 .so 文件
void *handle = dlopen(MYLIB_PATH, RTLD_NOW);
if (handle == NULL)
{
cerr << "Open library " << MYLIB_PATH << " error: " << dlerror() << endl;
return -1;
}
// 查找函数foobar,并返回函数指针
void (*fn_foobar)(int);
fn_foobar = (void (*)(int))dlsym(handle, "foobar");
char *error = NULL;
if ((error = dlerror()) != NULL)
{
cerr << "Symbol foobar not found: " << error << endl;
dlclose(handle);
return -2;
}
// 调用对应的foobar函数打印输出
fn_foobar(123);
dlclose(handle);
return 0;
}

编译运行效果:

[steven@sasd c++]$ g++-ldl -o ex_dload_so ex_dload_so.cpp
  [steven@sasd c++]$ ./ex_dload_so
  Printing from mylib.so 123
  [steven@sasdc++]$

4,使用c++创建和使用含有class的.so,采用多态机制实现接口发布

1)生成动态链接库triangle.so文件

// polygon.h
#ifndef __POLYGON_H__
#define __POLYGON_H__
class Polygon
{
protected:
double m_side_len;
public:
Polygon() : m_side_len(0) {}
virtual ~Polygon() {}
void set_side_len(double side_len)
{
m_side_len = side_len;
}
virtual double area() const = 0;
};
typedef Polygon* create_t();
typedef void destroy_t(Polygon*);
#endif /* __POLYGON_H__ */

// triangle.cpp
#include "polygon.h"
#include <cmath>
class Triangle: public Polygon
{
public:
virtual double area() const
{
return m_side_len * m_side_len * sqrt(3) / 2;
}
};
extern "C" Polygon* create()
{
return new Triangle;
}
extern "C" void destroy(Polygon *p)
{
delete p;
}

编译生成 triangle.so

[steven@sasd c++]$ g++ -fPIC -shared -o triangle.so triangle.cpp

2) 使用该动态链接库

// ex_dload_cppso.cpp
#include <iostream>
#include <string>
#include <dlfcn.h>
using namespace std;
#include "polygon.h"
int main()
{
// load triangle library
void *triangle = dlopen("./triangle.so", RTLD_LAZY);
if (!triangle)
{
cerr << "Can not load library: " << dlerror() << endl;
return -1;
}
// reset errors
dlerror();
// load the symbols "create"
create_t *create_triangle = (create_t*) dlsym(triangle, "create");
const char *dlsym_err = dlerror();
if (dlsym_err)
{
cerr << "Can not load symbol create: " << dlsym_err << endl;
return -2;
}
// load the symbols "destroy"
destroy_t* destroy_triangle = (destroy_t*) dlsym(triangle, "destroy");
dlsym_err = dlerror();
if (dlsym_err)
{
cerr << "Can not load symbol destroy: " << dlsym_err << endl;
return -2;
}
// create an instance of class <Trangle>
Polygon *poly = create_triangle();
// set side length of <Triagle>
poly->set_side_len(8);
cout << "The area is " << poly->area() << endl;
// destroy the class
destroy_triangle(poly);
// unload the triangle.so library
dlclose(triangle);
return 0;
}

编译运行:

[steven@sasd c++]$ g++ -ldl -o ex_dload_cppso ex_dload_cppso.cpp
  [steven@sasd c++]$ ./ex_dload_cppso
  The area is 55.4256
  [steven@sasd c++]$
   
2934 次浏览       19
相关文章

深度解析:清理烂代码
如何编写出拥抱变化的代码
重构-使代码更简洁优美
团队项目开发"编码规范"系列文章
相关文档

重构-改善既有代码的设计
软件重构v2
代码整洁之道
高质量编程规范
相关课程

基于HTML5客户端、Web端的应用开发
HTML 5+CSS 开发
嵌入式C高质量编程
C++高级编程
最新活动计划
LLM大模型应用与项目构建 12-26[特惠]
QT应用开发 11-21[线上]
C++高级编程 11-27[北京]
业务建模&领域驱动设计 11-15[北京]
用户研究与用户建模 11-21[北京]
SysML和EA进行系统设计建模 11-28[北京]
Visual C++编程命名规则
任何时候都适用的20个C++技巧
C语言进阶
串口驱动分析
轻轻松松从C一路走到C++
C++编程思想
更多...   


C++并发处理+单元测试
C++程序开发
C++高级编程
C/C++开发
C++设计模式
C/C++单元测试


北京 嵌入式C高质量编程
中国航空 嵌入式C高质量编程
华为 C++高级编程
北京 C++高级编程
丹佛斯 C++高级编程
北大方正 C语言单元测试
罗克韦尔 C++单元测试
更多...