转自http://blog.csdn.net/bailyzheng/article/details/17613847 & http://www.cnblogs.com/Anker/p/3746802.html
1、函数简介
dlopen
基本定义
功能:打开一个动态链接库
包含头文件: #include <dlfcn.h>
函数定义: void * dlopen( const char * pathname, int mode );
函数描述: 在dlopen的()函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。使用dlclose()来卸载打开的库。
mode:分为这两种
RTLD_LAZY 暂缓决定,等有需要时再解出符号
RTLD_NOW 立即决定,返回前解除所有未决定的符号。
RTLD_LOCAL
RTLD_GLOBAL 允许导出符号
RTLD_GROUP
RTLD_WORLD
返回值:
打开错误返回NULL
成功,返回库引用
编译时候要加入 -ldl (指定dl库)
dlsym()
功能:
dlclose()
编译参数 gcc -fPIC -shared
例如将如下程序编译为动态链接库libcaculate.so,程序如下:
int add(int a,int b){ return (a + b);}int sub(int a, int b){ return (a - b);}int mul(int a, int b){ return (a * b);}int div(int a, int b){ return (a / b);}
编译如下: gcc -fPIC -shared caculate.c -o libcaculate.so
#include#include #include //动态链接库路径#define LIB_CACULATE_PATH "./libcaculate.so"//函数指针typedef int (*CAC_FUNC)(int, int);int main(){ void *handle; char *error; CAC_FUNC cac_func = NULL; //打开动态链接库 handle = dlopen(LIB_CACULATE_PATH, RTLD_LAZY); if (!handle) { fprintf(stderr, "%s\n", dlerror()); exit(EXIT_FAILURE); } //清除之前存在的错误 dlerror(); //获取一个函数/*void *dlsym(void *handle, const char *symbol);返回值为void*(void **)&(cac_func)是将函数指针的地址强制转换void**类型然后使用*取值,获取dlsym的返回值实际这个地方没有必要这样,函数指针本来就是地址,可以直接用cac_func = dlsym(handle, "add");*/ *(void **) (&cac_func) = dlsym(handle, "add"); if ((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", error); exit(EXIT_FAILURE); } printf("add: %d\n", (*cac_func)(2,7)); cac_func = (CAC_FUNC)dlsym(handle, "sub"); printf("sub: %d\n", cac_func(9,2)); cac_func = (CAC_FUNC)dlsym(handle, "mul"); printf("mul: %d\n", cac_func(3,2)); cac_func = (CAC_FUNC)dlsym(handle, "div"); printf("div: %d\n", cac_func(8,2)); //关闭动态链接库 dlclose(handle); exit(EXIT_SUCCESS);}
编译选项如下:gcc -rdynamic -o main main.c -ldl
测试结果如下所示:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
另一种方式是利用dlsym 找到库中提供全局变量
#include#include #include #include typedef struct { const char *module; int (*GetValue)(char *pszVal); int (*PrintfHello)(); } hello_ST_API; int GetValue(char *pszVal) { int retval = -1; if (pszVal) retval = sprintf(pszVal, "%s", "123456"); printf("%s, %d, pszVer = %s\n", __FUNCTION__, __LINE__, pszVal); return retval; }int PrintfHello() { int retval = -1; printf("%s, %d, hello everyone\n", __FUNCTION__, __LINE__); return 0; }const hello_ST_API Hello = { .module = "hello", GetValue, PrintfHello, };
编译的时候用指令:
上面的函数是用一个全局结构体hello来指向。在dlsym定义中说不仅可以获取函数的地址,还可以获取全局变量的地址。所以此处是想通过dlsym来获取全局变量的地址。好处自己慢慢体会。
然后是dlopen代码
#include#include #include #include #include typedef struct { const char *module; int (*GetValue)(char *pszVal); int (*PrintfHello)(); } hello_ST_API; int main(int argc, char **argv) { hello_ST_API *hello; int i = 0; void *handle; char psValue[20] = { 0}; handle = dlopen(“库存放的绝对路径,你可以试试相对路径是不行的", RTLD_LAZY); if (! handle) { printf("%s,%d, NULL == handle\n", __FUNCTION__, __LINE__); return -1; } dlerror(); hello = dlsym(handle, "Hello"); if (!hello) { printf("%s,%d, NULL == handle\n", __FUNCTION__, __LINE__); return -1; } if (hello && hello->PrintfHello) i = hello->PrintfHello(); printf("%s, %d, i = %d\n", __FUNCTION__, __LINE__, i); if (hello && hello->GetValue) i = hello->GetValue(psValue); if (hello && hello->module) { printf("%s, %d, module = %s\n", __FUNCTION__, __LINE__, hello->module); } dlclose(handle); return 0; }
编译指令:gcc -o test hello_dlopen.c -ldl
运行./test结果如下。
PrintfHello, 27, hello everyone main, 36, i = 0 GetValue, 19, pszVer = 123456 main, 42, module = hello
可以看到结果正常出来了。
dlsym找到全局结构体hello后,可以直接用这个全局结构体指针来使用库里面的函数了,
因为我们有时候提供的库不仅仅是一个两个函数的,一般的一个库都会存在多个函数,用这种方式就可以直接使用了。不然找函数名称的话要写多少个dlsym啊?