dlopen功能:打开动态链接库
包含头文件: #include dlfcn.h
函数定义: void*dlopen(constchar*pathname,int mode );
函数描述: dlopen的) )函数以指定模式打开指定的动态连接库文件,并将句柄返回给调用过程。 使用dlclose ()卸载打开的库。
模式:
RTLD_LAZY保留决定,必要时解开符号
RTLD_NOW立即决定,在返回之前解除所有未决定符号。
RTLD_LOCAL
RTLD_GLOBAL允许您导出符号
RTLD_GROUP
RTLD_WORLD
返回值:
打开错误并返回空值
成功了。 返回到库引用
在编译时指定- LDL (指定dl库)
例如
gcc test.c -o test -ldl
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函数将函数指针p mytest指向mytest函数。
pmytest=(void(* ) ) ) dlsym ) phandle,' mytest ' );
dlclose
dlclose ()包含头文件。 #include dlfcn.h
函数原型为:intdlclose(void*handle );
函数说明: dlclose用于关闭指定句柄的动态链接库。 仅当此动态链接库的使用数量为0时,才会实际从系统中卸载。
dlerror dlerror ()包含头文件。 #include dlfcn.h
函数原型:常量字符*错误(void );
函数说明:如果动态链接库操作函数执行失败,dlerror可以返回错误消息。 如果返回值为NULL,则表示操作函数执行成功。
如果在编译函数源程序时选择-shared选项,则会创建动态链接库。 建议您在公共库目录(如/lib、/usr/lib )下使用. so后缀编写用户界面文件,以便其他用户共享。 要使用动态链接库,请在源程序中包含dlfcn.h头文件,编写程序时注意正确调用dlopen等函数,然后使用-rdynamic和-ldl选项动态调用
样品1
//foo.c Load the math library,andprintthecosineof 2.0: # include stdio.h # include stdlib.h # include dlfcn.h # define char *error; CAC_FUNC cac_func=NULL;
//打开动态链接库 handle = dlopen(LIB_CACULATE_PATH, RTLD_LAZY); if (!handle) { fprintf(stderr, "%sn", dlerror()); exit(EXIT_FAILURE); } //清除之前存在的错误 dlerror(); //获取一个函数 *(void **) (&cac_func) = dlsym(handle, "add"); if ((error = dlerror()) != NULL) { fprintf(stderr, "%sn", error); exit(EXIT_FAILURE); } printf("add: %dn", (*cac_func)(2,7)); cac_func = (CAC_FUNC)dlsym(handle, "sub"); printf("sub: %dn", cac_func(9,2)); cac_func = (CAC_FUNC)dlsym(handle, "mul"); printf("mul: %dn", cac_func(3,2)); cac_func = (CAC_FUNC)dlsym(handle, "div"); printf("div: %dn", cac_func(8,2)); //关闭动态链接库 dlclose(handle); return 0; } gcc -rdynamic -o foo foo.c -ldl示例二
//主程序#include <stdlib.h>#include <dlfcn.h>#include <stdio.h>//gddxg结构体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>//gddxg结构体类型typedef struct __test { int i; void (*echo_fun)(struct __test *p);}Test;//gddxg注册函数原型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 = %sn", __FUNCTION__, __LINE__, pszVal); return retval;}int PrintfHello(){ int retval = -1; printf("%s, %d, hello everyonen", __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 == handlen", __FUNCTION__, __LINE__); return -1; } dlerror(); hello = dlsym(handle, "Hello"); if (!hello) { printf("%s,%d, NULL == handlen", __FUNCTION__, __LINE__); return -1; } if (hello && hello->PrintfHello) i = hello->PrintfHello(); printf("%s, %d, i = %dn", __FUNCTION__, __LINE__, i); if (hello && hello->GetValue) i = hello->GetValue(psValue); if (hello && hello->module) { printf("%s, %d, module = %sn", __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