Linux提供了一套API来动态装载库。下面列出了这些API:

- dlopen,打开一个库,并为使用该库做些准备。
- dlsym,在打开的库中查找符号的值。
- dlclose,关闭库。
- dlerror,返回一个描述最后一次调用dlopen、dlsym,或dlclose的错误信息的字符串。

C语言用户需要包含头文件dlfcn.h才能使用上述API。glibc还增加了两个POSIX标准中没有的API:
- dladdr,从函数指针解析符号名称和所在的文件。
- dlvsym,与dlsym类似,只是多了一个版本字符串参数。

在Linux上,使用动态链接的应用程序需要和库libdl.so一起链接,也就是使用选项-ldl。但是,编译时不需要和动态装载的库一起链接。程序3-1是一个在Linux上使用dl*例程的简单示例。

延迟重定位(Lazy Relocation)
延迟重定位/装载是一个允许符号只在需要时才重定位的特性。这常在各UNIX系统上解析函数调用时用到。当一个和共享库一起链接的应用程序几乎不会用到该共享库中的函数时,该特性被证明是非常有用的。这种情况下,只有库中的函数被应用程序调用时,共享库才会被装载,否则不会装载,因此会节约一些系统资源。但是如果把环境变量LD_BIND_NOW设置成一个非空值,所有的重定位操作都会在程序启动时进行。也可以在链接器命令行通过使用-z now链接器选项使延迟绑定对某个特定的共享库失效。需要注意的是,除非重新链接该共享库,否则对该共享库的这种设置会一直有效。

初始化(initializing)和终止化(finalizing)函数
有时候,以前的代码可能用到了两个特殊的函数:_init和_fini。_init和_fini函数用在装载和卸载某个模块(注释14)时分别控制该模块的构造器和析构器(或构造函数和析构函数)。他们的C语言原型如下:
void _init(void);
void _fini(void);
当一个库通过dlopen()动态打开或以共享库的形式打开时,如果_init在该库中存在且被输出出来,则_init函数会被调用。如果一个库通过dlclose()动态关闭或因为没有应用程序引用其符号而被卸载时,_fini函数会在库卸载前被调用。当使用你自己的_init和_fini函数时,需要注意不要与系统启动文件一起链接。可以使用GCC选项 -nostartfiles 做到这一点。
但是,使用上面的函数或GCC的-nostartfiles选项并不是很好的习惯,因为这可能会产生一些意外的结果。相反,库应该使用__attribute__((constructor))和__attribute__((destructor))函数属性来输出它的构造函数和析构函数。如下所示:
void __attribute__((constructor)) x_init(void)
void __attribute__((destructor)) x_fini(void)
构造函数会在dlopen()返回前或库被装载时调用。析构函数会在这样几种情况下被调用:dlclose()返回前,或main()返回后,或装载库过程中exit()被调用时。

我们通过一个例子来讲解dlopen系列函数的使用和操作:

主程序:

  1. #include <stdlib.h>
  2. #include <dlfcn.h>
  3. #include <stdio.h>
  4. //申明结构体
  5. typedef struct __test {
  6. int i;
  7. void (* echo_fun)(struct __test *p);
  8. }Test;
  9. //供动态库使用的注册函数
  10. void __register(Test *p) {
  11. p->i = 1;
  12. p->echo_fun(p);
  13. }
  14. int main(void) {
  15. void *handle = NULL;
  16. char *myso = "./mylib.so";
  17. if((handle = dlopen(myso, RTLD_NOW)) == NULL) {
  18. printf("dlopen - %sn", dlerror());
  19. exit(-1);
  20. }
  21. return 0;
  22. }

动态库:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. //申明结构体类型
  4. typedef struct __test {
  5. int i;
  6. void (*echo_fun)(struct __test *p);
  7. }Test;
  8. //申明注册函数原型
  9. void __register(Test *p);
  10. static void __printf(Test *p) {
  11. printf("i = %dn", p->i);
  12. }
  13. //动态库申请一个全局变量空间
  14. //这种 ".成员"的赋值方式为c99标准
  15. static Test config = {
  16. .i = 0,
  17. .echo_fun = __printf,
  18. };
  19. //加载动态库的自动初始化函数
  20. void _init(void) {
  21. printf("initn");
  22. //调用主程序的注册函数
  23. __register(&config);
  24. }

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

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

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

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

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

dlopen函数详解的更多相关文章

  1. dlopen代码详解——从ELF格式到mmap

    最近一个月的时间大部分在研究glibc中dlopen的代码,基本上对整个流程建立了一个基本的了解.由于网上相关资料比较少,走了不少弯路,故在此记录一二,希望后人能够站在我这个矮子的肩上做出精彩的成果. ...

  2. malloc 与 free函数详解<转载>

    malloc和free函数详解   本文介绍malloc和free函数的内容. 在C中,对内存的管理是相当重要.下面开始介绍这两个函数: 一.malloc()和free()的基本概念以及基本用法: 1 ...

  3. NSSearchPathForDirectoriesInDomains函数详解

    NSSearchPathForDirectoriesInDomains函数详解     #import "NSString+FilePath.h" @implementation ...

  4. JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解

    二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...

  5. Linux C popen()函数详解

    表头文件 #include<stdio.h> 定义函数 FILE * popen( const char * command,const char * type); 函数说明 popen( ...

  6. kzalloc 函数详解(转载)

    用kzalloc申请内存的时候, 效果等同于先是用 kmalloc() 申请空间 , 然后用 memset() 来初始化 ,所有申请的元素都被初始化为 0. view plain /** * kzal ...

  7. Netsuite Formula > Oracle函数列表速查(PL/SQL单行函数和组函数详解).txt

    PL/SQL单行函数和组函数详解 函数是一种有零个或多个参数并且有一个返回值的程序.在SQL中Oracle内建了一系列函数,这些函数都可被称为SQL或PL/SQL语句,函数主要分为两大类: 单行函数 ...

  8. jQuery.attr() 函数详解

    一,jQuery.attr()  函数详解: http://www.365mini.com/page/jquery-attr.htm 二,jQuery函数attr()和prop()的区别: http: ...

  9. memset函数详解

    语言中memset函数详解(2011-11-16 21:11:02)转载▼标签: 杂谈 分类: 工具相关  功 能: 将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值, 块的大 ...

随机推荐

  1. repeat帮定删除按钮事件,并且生成去人删除提示

    前台 <ItemTemplate> <tr> <td> <asp:LinkButton ID="LinkButton_cancel" On ...

  2. Javascript调用 ActiveXObject导出excel文档。

    function makeDataBook(){ var xls = new ActiveXObject ("Excel.Application"); xls.visible = ...

  3. Entity Framework 学习高级篇2—改善EF代码的方法(下)

    ,IQueryable<Customers>>( (database) => database.Customers.Where(c => c.City == " ...

  4. MySQL-测试卷一

    MySQL-测试卷一 一.单项选择题 1 下面不属于Msql数据库特点的是(  ) A. 免费使用  B.不能跨平台  C.开源软件  D.功能强大 2 定义表的一个字段, 要求能表示4位整数,2位小 ...

  5. .htaccess重写URL讲解

    使用ThinkPHP和Laravel等框架的都知道,所以的请求都需要经过index.php文件入口,无论你的URI是什么. 当然除了访问的是静态文件或者访问路径的文件真实存在,例如你访问xxx.com ...

  6. Update Case的用法与execute执行字符串

    摘自于网路:http://www.cnblogs.com/joinger/articles/1297160.html update h_crm_SafetyAccessUser set         ...

  7. Htttp协议

    我 们在浏览器的地址栏里输入的网站地址叫做URL(UniformResourceLocator,统一资源定位符).就像每家每户都有一个门牌地址一样, 每个网页也都有一个Internet地址.当你在浏览 ...

  8. zzuli 1907: 小火山的宝藏收益 邻接表+DFS

    Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 113  Solved: 24 SubmitStatusWeb Board Description    ...

  9. if __name__ == '__main__'在python中的应用

    当你打开一个.py文件时,经常会在代码的最下面看到if __name__ == '__main__':,现在就来介 绍一下它的作用. 模块是对象,并且所有的模块都有一个内置属性 __name__.一个 ...

  10. 在windows系统用odbc连接

    当连接的数据出现失败时,出现数据库别名仍然存在,但还是要用这个别名重新建立连接 在windows客户端,用输入db2cmd输入c:\Users\yexuxia>db2 list db direc ...