使用c++编写和使用.so动态链接库

Natalia ·
更新时间:2024-09-21
· 516 次阅读

  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 ", 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表示要查找的符号的名字,一个以‘’ 结尾的字符串;   如果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,采用多态机制实现接口发布,示例摘自《C++ dlopen mini HOWTO》   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++]$

 



.so c+ C++

需要 登录 后方可回复, 如果你还没有账号请 注册新账号