一、引言
dlopen()是一个强大的库函数。该函数将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。
比如 Apache Web 服务器利用某个函数在运行过程中加载模块,这为它提供了额外的能力。一个配置文件控制了加载模块的过程。这种机制使得在系统中添加或者删除一个模块时,都不需要重新编译了。
二、函数
2.1、dlopen()
可以在自己的程序中使用 dlopen()。dlopen() 在 dlfcn.h 中定义,并在 dl 库中实现。
在dlopen函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。使用dlclose来卸载打开的库。
#包含头文件:
#include <dlfcn.h>
# 函数定义:
void * dlopen( const char * pathname, int mode );
mode:分为这两种
RTLD_LAZY 暂缓决定,等有需要时再解出符号
RTLD_NOW 立即决定,返回前解除所有未决定的符号。
RTLD_LOCAL
RTLD_GLOBAL 允许导出符号
RTLD_GROUP
RTLD_WORLD
返回值:
打开错误返回NULL
成功,返回库引用
编译时候要加入 -ldl (指定dl库) :
例如 :gcc test.c -o test -ldl
2.2、dlsym()
当库被装入后,可以把 dlopen() 返回的句柄作为给 dlsym() 的第一个参数,以获得符号在库中的地址。使用这个地址,就可以获得库中特定函数的指针,并且调用装载库中的相应函数。
该函数在<dlfcn.h>文件中,dlsym()的函数原型是 :
void* dlsym(void* handle,const char* symbol)
handle是由dlopen打开动态链接库后返回的指针;
symbol就是要求获取的函数的名称;
dlsym根据动态链接库操作句柄(pHandle)与符号(symbol),返回符号对应的地址;
使用这个函数不但可以获取函数地址,也可以获取变量地址。
比如,假设在so中
定义了一个void mytest()函数,那在使用so时先声明一个函数指针:
void (*pMytest)();
然后使用dlsym函数将函数指针pMytest指向mytest函数,
pMytest = (void (*)())dlsym(pHandle, "mytest");
2.3、dlclose()
包含头文件:
#include <dlfcn.h>
函数原型为:
int dlclose (void *handle);
函数描述:
dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
2.4、dlerror()
包含头文件:
#include <dlfcn.h>
函数原型:
const char *dlerror(void);
函数描述:
当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。
三、创建与使用动态链接库
LINUX创建与使用动态链接库并不是一件难事。
编译函数源程序时选用-shared选项即可创建动态链接库,注意应以.so后缀命名,最好放到公用库目录(如/lib,/usr/lib等)下面,并要写好用户接口文件,以便其它用户共享。
使用动态链接库,源程序中要包含dlfcn.h头文件,写程序时注意dlopen等函数的正确调用,编译时要采用-rdynamic选项与-ldl选项 ,以产生可调用动态链接库的执行代码。
四、实例
库代码:
#include <stdio.h>
void hello(void)
{
printf("hello\n");
}
编译命令:
gcc -fPIC -shared -o hello.so hello.c
使用库的代码:
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int argc, char **argv)
{
void *handle;
void (*callfun)();
char *error;
handle = dlopen("./hello.so",RTLD_LAZY); //如果hello.so不是在LD_LIBRARY_PATH所申明
//的路径中必须使用全路径名
if(!handle)
{
printf("%s \n",dlerror());
exit(1);
}
dlerror();
callfun=dlsym(handle,"hello");
if((error=dlerror())!=NULL)
{
printf("%s \n",error);
exit(1);
}
callfun();
dlclose(handle);
return 0;
}
编译命令:
gcc -o hello_dlopen hello_dlopen.c -ldl
执行:./hello_dlopen
从中可以体会到编译时不需要库的好处。
另外一种在编译的时候需要动态库的使用方法:
https://blog.csdn.net/weixin_38932035/article/details/105270998