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库) 
  例如 
  gcc test.c -o test -ldl

解析方式:RTLD_LAZY:暂缓决定,在dlopen返回前,对于动态库中的未定义的符号不执行解析(只对函数引用有效,对于变量引用总是立即解析)

RTLD_NOW:立即决定,在dlopen返回前,解析出所有未定义的符号,如果解析不出来,在dlopen会返回NULL,错误为 undefined symbol:XXX...

作用范围:RTLD_GLOBAL: 动态库中定义的符号可被其后打开的其他库重定位

RTLD_LOCAL: 与RTLD_GLOBAL作用相反,动态库中定义的符号不能被其后打开的其他库重定位。如果没有指明是RTLD_GLOBAL还是RTLD_LOCAL,那么

默认是RTLD_LOCAL。

作用方式:RTLD_NODELETE:在dlclose()期间不卸载库,并且在以后使用dlopen()重新加载库时不初始化库中的静态变量。这个flag不是POSIX-2001标准。

RTLD_NOLOAD: 不加载库,可用于测试库是否已经加载(dlopen()返回NULL说明未加载,否则说明加载),也可用于改变已加载库的flag,

如:先前加载库的flag为RTLD_LOCAL,用dlopen(RTLD_NOLOAD|RTLD_GLOBAL)后flag将变成RTLD_GLOBAL.这个flag不是POSIX-2001标准

RTLD_DEEPBIND:在搜索全局符号前先搜索库内的符号,避免同名符号的冲突,这个flag不是POSIX-2001标准

  dlopen()是一个强大的库函数。该函数将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。比如 Apache Web 服务器利用这个函数在运行过程中加载模块,这为它提供了额外的能力。一个配置文件控制了加载模块的过程。这种机制使得在系统中添加或者删除一个模块时,都不需要重新编译了。 
  可以在自己的程序中使用 dlopen()。dlopen() 在 dlfcn.h 中定义,并在 dl 库中实现。它需要两个参数:一个文件名和一个标志。文件名可以是我们学习过的库中的 soname。标志指明是否立刻计算库的依赖性。如果设置为 RTLD_NOW 的话,则立刻计算;如果设置的是 RTLD_LAZY,则在需要的时候才计算。另外,可以指定 RTLD_GLOBAL,它使得那些在以后才加载的库可以获得其中的符号。

  当库被装入后,可以把 dlopen() 返回的句柄作为给 dlsym() 的第一个参数,以获得符号在库中的地址。使用这个地址,就可以获得库中特定函数的指针,并且调用装载库中的相应函数。

dlsym
  

dlsym()的函数原型是 void* dlsym(void* handle,const char* symbol) 该函数在<dlfcn.h>文件中。 
handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数的名称,函数返回值是void*,指向函数的地址,供调用使用

取动态对象地址:
#include <dlfcn.h>
void *dlsym(void *pHandle, char *symbol);
dlsym根据动态链接库操作句柄(pHandle)与符号(symbol),返回符号对应的地址。
使用这个函数不但可以获取函数地址,也可以获取变量地址。比如,假设在so中定义了一个void mytest()函数,那在使用so时先声明一个函数指针:void (*pMytest)(),然后使用dlsym函数将函数指针pMytest指向mytest函数,
pMytest = (void (*)())dlsym(pHandle, "mytest");

dlclose

dlclose() 包含头文件: #include <dlfcn.h> 
函数原型为: int dlclose (void *handle); 
函数描述: dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。

dlerror

dlerror() 包含头文件: #include <dlfcn.h> 
函数原型: const char *dlerror(void); 
函数描述: 当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。

编译函数源程序时选用-shared选项即可创建动态链接库,注意应以.so后缀命名,最好放到公用库目录(如/lib,/usr/lib等)下面,并要写好用户接口文件,以便其它用户共享。使用动态链接库,源程序中要包含dlfcn.h头文件,写程序时注意dlopen等函数的正确调用,编译时要采用-rdynamic选项与-ldl选项 ,以产生可调用动态链接库的执行代码。

示例一

//foo.c    Load the math library, and print the cosine of 2.0:
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>#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 **) (&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);return 0;}gcc -rdynamic -o foo foo.c -ldl

示例二

//主程序
#include <stdlib.h>
#include <dlfcn.h>
#include <stdio.h>//申明结构体
typedef struct __test {int i;void (* echo_fun)(struct __test *p);
}Test;//供动态库使用的注册函数
void __register(Test *p) {p->i = 1;p->echo_fun(p);
}int main(void) {void *handle = NULL;char *myso = "./mylib.so";if((handle = dlopen(myso, RTLD_NOW)) == NULL) {printf("dlopen - %sn", dlerror());exit(-1);}return 0;
}
//动态库
#include <stdio.h>
#include <stdlib.h>//申明结构体类型
typedef struct __test {int i;void (*echo_fun)(struct __test *p);
}Test;//申明注册函数原型
void __register(Test *p);static void __printf(Test *p) {printf("i = %dn", p->i);
}//动态库申请一个全局变量空间。这种 ".成员"的赋值方式为c99标准
static Test config = {.i = 0,.echo_fun = __printf,
};//加载动态库的自动初始化函数
void _init(void) {printf("initn");__register(&config);//调用主程序的注册函数
}

动态库编译: gcc -shared -fPIC -nostartfiles -o mylib.so mylib.c

主程序编译: gcc test.c -ldl -rdynamic

主程序通过dlopen()加载一个.so的动态库文件, 然后动态库会自动运行 _init() 初始化函数, 初始化函数打印一个提示信息, 然后调用主程序的注册函数给结构体重新赋值, 然后调用结构体的函数指针, 打印该结构体的值. 这样就充分的达到了主程序和动态库的函数相互调用和指针的相互传递.

gcc参数 -rdynamic 用来通知链接器将所有符号添加到动态符号表中(目的是能够通过使用 dlopen 来实现向后跟踪).

gcc参数 -fPIC 作用: 当使用.so等类的库时,当遇到多个可执行文件共用这一个库时, 在内存中,这个库就不会被复制多份,让每个可执行文件一对一的使用,而是让多个可执行文件指向一个库文件,达到共用. 宗旨:节省了内存空间,提高了空间利用率.

示例三

//生成动态库 hello.c函数原型:
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>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,
};编译的时候用指令:gcc -shared -o hello.so hello.c
上面的函数是用一个全局结构体hello来指向。在dlsym定义中说不仅可以获取函数的地址,还可以获取全局变量的地址。所以此处是想通过dlsym来获取全局变量的地址。好处自己慢慢体会。//dlopen代码
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>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

LINUX下动态链接库的使用(dlopen/dlsym/dlclose/dlerror)相关推荐

  1. LINUX下动态链接库的使用-dlopen dlsym dlclose dlerror

    dlopen 基本定义 功能:打开一个动态链接库  包含头文件:  #include <dlfcn.h>  函数定义:  void * dlopen( const char * pathn ...

  2. LINUX下动态链接库的使用-dlopen dlsym dlclose dlerror【zt】

    dlopen 基本定义 功能:打开一个动态链接库 包含头文件: #include <dlfcn.h> 函数定义: void * dlopen( const char * pathname, ...

  3. linux 下动态链接库的创建与使用——dlopen,dlsym

     一.引言 通常情况下,对函数库的链接是放在编译时期(compile time)完成的.所有相关的对象文件(object file)与牵涉到的函数库(library)被链接合成一个可执行文件(exec ...

  4. 《LINUX下动态链接库的创建与应用》

    大家都知道,在windows系统中有很多的动态链接库(以.dll为后缀的文档,dll即dynamic link library).这种动态链接库,和静态函数库不同,他里面的函数并不是执行程式本身的一部 ...

  5. Linux下动态链接库调用

    Linux下动态链接库调用 2014-11-01 10:39 3人阅读 评论(0) 收藏 编辑 删除 Linux下动态链接库调用 2013-06-08 20:52:48|  分类:集成开发环境相关 | ...

  6. linux path环境变量检索目录,Linux下动态链接库加载路径及搜索路径问题

    引子 近日,服务器迁移后,偷懒未重新编译nginx的,直接./nginx启动,结果遇到如下问题: "error while loading shared libraries" 这是 ...

  7. linux查看进程加载了哪些dll,linux下动态链接库的加载及解析过程

    http://hi.baidu.com/hust_chen/blog/item/54a8c516231d0c0ec93d6d3e.html linux下动态链接库的加载及解析过程(ZZ) 2008-1 ...

  8. Linux下动态链接库的查找问题

    Linux下动态链接库的查找问题 上一篇文章我们从 Linux C 编程的角度分析了一下 Linux 中的静态链接库和动态链接库的区别,这篇文章着重从 Linux 编译和运行的角度分析一下 Linux ...

  9. dlopen dlsym dlclose加载动态链接库

    dlopen 在dlopen()函数以指定模式打开指定的动态链接库文件,并返回一个句柄给dlsym()的调用进程.使用dlclose()来卸载打开的库. 基本定义 功能:打开一个动态链接库,并返回动态 ...

最新文章

  1. 究竟深度学习在干什么?
  2. Programming Pearls: Chatper3 Problem5 [Hyphenation Words]
  3. 小功率电子镇流荧光灯相关实验
  4. 用 Flask 来写个轻博客 (6) — (M)VC_models 的关系(one to many)
  5. Python 基础教程:位运算的奥妙
  6. 艾媒咨询:泛娱乐「体验共享」报告发布,网易云信多个案例领衔
  7. 基于Boost::beast模块的快速WebSocket服务器
  8. 打jar包和执行jar包
  9. MySQL生产常用SQL语句汇总
  10. Sqlserver2005迁移至Oracle系列之二:生成存储过程
  11. jPList – 实现灵活排序和分页功能的 jQuery 插件
  12. R语言︱缺失值处理之多重插补——mice包
  13. 使用VSCode玩转Java (二)
  14. DM365的UBL源码分析(二)
  15. vue 判断字符串是否是英文_Vuejs Element input组件区分中英文 统计长度
  16. 参加软件测试培训找工作有前景吗
  17. QTreeWidget实现搜索子节点功能
  18. c语言 mysql 连接字符串,C语言连接MySQL数据库
  19. 问卷星简单自动填写调查问卷
  20. linux下.tar.gz如何解压

热门文章

  1. ajax回调函数onreadystatechange
  2. 电信运营商网络运维方案
  3. 全景视频如何下载 - 免费绿色纯净无广告的全景视频下载软件
  4. 回归问题:身高与体重的多项式回归(二次回归)分析
  5. 工艺路线和工序有差别吗_数控加工 工艺有多难?看完这套完整的流程你就会了!...
  6. “善行天下 玉满中华—中国珠宝玉石原石交易平台第三批交易品上市敲钟仪式”新闻发布会
  7. vim配置vimrc
  8. JS OffsetParent属性
  9. keil中添加stc单片机芯片包的方法
  10. .jpg图片转化流程详解