电工技术基础_电工基础知识_电工之家-电工学习网

欢迎来到电工学习网!

dlopen函数用法详解

2017-04-17 09:23分类:电子技术 阅读:

 

dlopen函数用法详解
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系列函数的运用和操作:

主程序:

#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 test.c -ldl -rdynamic

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

主程序经过dlopen()加载一个.so的动态库文件, 然后动态库会主开作业 _init() 初始化函数, 初始化函数打印一个提示信息, 然后调用主程序的注册函数给构造体从头赋值, 然后调用构造体的函数指针, 打印该构造体的值. 这么就充沛的抵达了主程序和动态库的函数互相调用和指针的互相传递.

gcc参数 -rdynamic 用来告诉联接器将悉数符号添加到动态符号表中(意图是可以经过运用 dlopen 来结束向后盯梢).

gcc参数 -fPIC 效果: 当运用.so等类的库时,当遇到多个可施行文件共用这一个库时, 在内存中,这个库就不会被仿制多份,让每个可施行文件一对一的运用,而是让多个可施行文件指向一个库文件,抵达共用. 主旨:节省了内存空间,跋涉了空间运用率.

上一篇:光纤收发器互连疑问答复

下一篇:中兴olt c300检查光衰指令

相关推荐

电工推荐

    电工技术基础_电工基础知识_电工之家-电工学习网
返回顶部