
linux调用动态库so文件.doc
36页linux 调用动态库 so 文件分类: c++那些事 2012-05-02 14:55 4831 人阅读 评论(0) 收藏 举报linuxdlliostreamclass 编译器 winapi关于动态调用动态库方法说明 一、 动态库概述 1、 动态库的概念 日常编程中,常有一些函数不需要进行编译或者可以在多个文件中使用(如数据库输入/输出操作或屏幕控制等标准任务函数)可以事先对这些函数进行编译,然后将它们放置在一些特殊的目标代码文件中,这些目标代码文件就称为库库文件中的函数可以通过连接程序与应用程序进行链接,这样就不必在每次开发程序时都对这些通用的函数进行编译了动态库是一种在已经编译完毕的程序开始启动运行时,才被加载来调用其中函数的库其加载方式与静态库截然不同2、 动态库的命名 Linux 下,动态库通常以.so(share object)结尾 通常/lib 和/usr/lib 等目录下存在大量系统提供的以.so 结尾的动态库文件)Windows 下,动态库常以.dll 结尾 通常 C:\windows\System32 等目录下存在大量系统提供的以.dll 结尾的动态库文件)3、 动态库与静态库之间的区别 静态库是指编译连接时,把库文件的代码全部加入到可执行文件中,所以生成的文件较大,但运行时,就不再需要库文件了。
即,程序与静态库编译链接后,即使删除静态库文件,程序也可正常执行动态库正好相反,在编译链接时,没有把库文件的代码加入到可执行文件中,所以生成的文件较小,但运行时,仍需要加载库文件即,程序只在执行启动时才加载动态库,如果删除动态库文件,程序将会因为无法读取动态库而产生异常二、 Linux 下动态调用动态库 备注:以下 linux 实例说明都是在 RedHat 5.1 系统+ gcc 版本 4.1.2 20080704 (Red Hat 4.1.2-46)上实现1、 .so 动态库的生成 可使用 gcc 或者 g++编译器生成动态库文件( 此处以 g++编译器为例)g++ -shared -fPIC -c XXX.cppg++ -shared -fPIC -o XXX.so XXX.o2、 .so 动态库的动态调用接口函数说明 动态库的调用关系可以在需要调用动态库的程序编译时,通过 g++的-L 和-l 命令来指定例如:程序 test 启动时需要加载目录 /root/src/lib 中的 libtest_so1.so 动态库,编译命令可照如下编写执行:g++ -g -o test test.cpp –L/root/src/lib –ltest_so1(此处,我们重点讲解动态库的动态调用的方法,关于静态的通过 g++编译命令调用的方式不作详细讲解,具体相关内容可上网查询)Linux 下,提供专门的一组 API 用于完成打开动态库,查找符号,处理出错,关闭动态库等功能。
下面对这些接口函数逐一介绍(调用这些接口时,需引用头文件#include ):1) dlopen函数原型:void *dlopen(const char *libname,int flag);功能描述:dlopen 必须在 dlerror,dlsym 和 dlclose 之前调用,表示要将库装载到内存,准备使用如果要装载的库依赖于其它库,必须首先装载依赖库如果 dlopen 操作失败,返回 NULL 值;如果库已经被装载过,则 dlopen 会返回同样的句柄参数中的 libname 一般是库的全路径,这样 dlopen 会直接装载该文件;如果只是指定了库名称,在 dlopen 会按照下面的机制去搜寻:a.根据环境变量 LD_LIBRARY_PATH 查找b.根据/etc/ld.so.cache 查找c.查找依次在/lib 和/usr/lib 目录查找flag 参数表示处理未定义函数的方式,可以使用 RTLD_LAZY 或RTLD_NOWRTLD_LAZY 表示暂时不去处理未定义函数,先把库装载到内存,等用到没定义的函数再说;RTLD_NOW 表示马上检查是否存在未定义的函数,若存在,则 dlopen以失败告终。
2) dlerror函数原型:char *dlerror(void);功能描述:dlerror 可以获得最近一次 dlopen,dlsym 或 dlclose 操作的错误信息,返回NULL 表示无错误dlerror 在返回错误信息的同时,也会清除错误信息3) dlsym函数原型:void *dlsym(void *handle,const char *symbol);功能描述:在 dlopen 之后,库被装载到内存dlsym 可以获得指定函数(symbol) 在内存中的位置(指针) 如果找不到指定函数,则 dlsym 会返回 NULL 值但判断函数是否存在最好的方法是使用 dlerror 函数,4) dlclose函数原型:int dlclose(void *);功能描述:将已经装载的库句柄减一,如果句柄减至零,则该库会被卸载如果存在析构函数,则在 dlclose 之后,析构函数会被调用3、 普通函数的调用 此处以源码实例说明各源码文件关系如下:test_so1.h 和 test_so1.cpp 生成 test_so1.so 动态库test_so2.h 和 test_so2.cpp 生成 test_so2.so 动态库。
test_dl.cpp 生成 test_dl 可执行程序,test_dl 通过 dlopen 系列等 API 函数,并使用函数指针以到达动态调用不同 so 库中 test 函数的目的////////////////////////////////test_so1.h//////////////////////////////////////////////////////#include #include extern "C" {int test(void);}////////////////////////////////ttest_so1.cpp//////////////////////////////////////////////////////#include "test_so1.h"int test(void){printf("USING TEST_SO1.SO NOW!\n");//注意此处与 test_so2.cpp 中的//test 函数的不同return 1;}//////////////////////////////// test_so2.h //////////////////////////////////////////////////////#include #include extern "C" {int test(void);}////////////////////////////////ttest_so2.cpp//////////////////////////////////////////////////////#include "test_so2.h"int test(void){printf("USING TEST_SO2.SO NOW!\n");//注意此处与 test_so1.cpp 中的//test 函数的不同return 1;}////////////////////////////////test_dl.cpp//////////////////////////////////////////////////////#include #include #include int main(int argc, char **argv){if(argc!=2){printf("Argument Error! You must enter like this:\n");printf("./test_dl test_so1.so\n");exit(1); }void *handle;char *error;typedef void (*pf_t)(); //声明函数指针类型handle = dlopen (argv[1], RTLD_NOW); //打开 argv[1]指定的动态库if (!handle){fprintf (stderr, "%s\n", dlerror());exit(1);}dlerror(); pf_t pf=(pf_t)dlsym(handle,"test" ); //指针 pf 指向 test 在当前内存中的地址if ((error = dlerror()) != NULL){fprintf (stderr, "%s\n", error);exit(1);} pf(); //通过指针 pf 的调用来调用动态库中的 test 函数dlclose(handle); //关闭调用动态库句柄return 0;}////////////////////////////////makefile//////////////////////////////////////////////////////.SUFFIXES: .c .cpp .oCC=g++ -shared -fPICGCC=g++all:test_so1.so test_so2.so test_dl cleanOBJ1=test_so1.oOBJ2=test_so2.oOBJ3=test_dl.otest_so1.so:$(OBJ1)$(CC) -o $@ $?cp $@ /usr/libtest_so2.so:$(OBJ2) $(CC) -o $@ $?cp $@ /usr/libtest_dl:$(OBJ3)$(GCC) -o $@ $? -ldl.cpp.o:$(CC) -c $*.cpp.c.o:$(CC) -c $*.cclean:rm -f *.o上述源程序中,需重点注意两个问题:1、test_dl.cpp 中,对于动态库中的 test 函数调用是通过函数指针来完成的。
2、test_so1.h 和 test_so2.h 中都使用了 extern "C"在每个 C++程序(或库、目标文件)中,所有非静态( non-static)函数在二进制文件中都是以“符号(symbol)”形式出现的这些符号都是唯一的字符串,从而把各个函数在程序、库、目标文件中区分开来在 C 中,符号名正是函数名: strcpy 函数的符号名就是“strcpy”这可能是因为两个非静态函数的名字一定各不相同的缘故而 C++允许重载(不同的函数有相同的名字但不同的参数),并且有很多 C 所没有的特性──比如类、成员函数、异常说明── 几乎不可能直接用函数名作符号名为了解决这个问题,C++采用了所谓的 name mangling它把函数名和一些信息(如参数数量和大小)杂糅在一起,改造成奇形怪状,只有编译器才懂的符号名例如,被 mangle 后的 foo 可能看起来像 foo@4%6^,或者,符号名里头甚至不包括“foo”其中一个问题是,C++标准(目前是 [ISO14882])并没有定义名字必须如何被 mangle,所以每个编译器都按自己的方式来进行 name mangling有些编译器甚至在不同版本间更换mangling 算法(尤其是 g++ 2.x 和 3.x)。
即使您搞清楚了您的编译器到底怎么进行mangling 的,从而可以用 dlsym 调用函数了,但可能仅仅限于您手头的这个编译器而已,而无法在下一版编译器下工作用 extern "C"声明的函数将使用函数名作符号名。





![河南新冠肺炎文件-豫建科[2020]63号+豫建科〔2019〕282号](http://img.jinchutou.com/static_www/Images/s.gif)






