-ld
如果你的程序中使用dlopen、dlsym、dlclose、dlerror 显示加载动态库,需要设置链接选项 -ldl 加载动态链接库,首先为共享库分配物理内存,然后在进程对应的页表项中建立虚拟页和物理页面之间的映射。你可以认为系统中存在一种引用计数机制, 每当一个进程加载了共享库(在该进程的页表中进行一次映射),引用计数加1;一个进程显式卸载(通过dlclose等)共享库或进程退出时,引用计数减1,当减少到0时,系统卸载共享库。
gcc -o main main.c -ldl
1.dlopen
头文件: #include <dlfcn.h>
函数定义:void *dlopen(const char* pathName, int mode);
功能: 打开指定名字(pathname)的动态链接库,并返回操作句柄。
参数: pathname:指的是db文件或listDB.so文件在实机环境中的位置
mode:指的是打开数据库的模式,mode在linux下,按功能有以下几种
解析方式:
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标准
返回值:
返回值void*,如果成功则返回引用这个数据库的句柄,如果失败则返回NULL
2.dlsym
头文件: #include <dlfcn.h>
函数定义:void *dlsym(void *handle, const char *symbol);
功能: 根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的地址,不但可以获取函数地址,也可以获取变量地址。
参数:
handle:由dlopen打开动态链接库后返回的指针;
symbol:要求获取的函数或全局变量的名称。
返回值:
void* 指向函数的地址,供调用使用。
3.dlclose
头文件: #include <dlfcn.h>
函数定义:int dlclose (void *handle)
功能: dlclose 用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
参数:
handle:由dlopen打开动态链接库后返回的指针。
4.dlerror
头文件: #include <dlfcn.h>
函数定义:const char* dlerror(void);
功能: 当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。
代码举例说明:
#include <stdio.h>
#include <dlfcn.h> // 包含动态链接库相关的头文件
typedef unsigned long (*PFN_SKF_ConnectDev)(char*, void*); //连接设备
int main()
{
void *handle; // 动态链接库句柄
void *hDev = NULL; //设备句柄
PFN_SKF_ConnectDev pfnSKF_ConnectDev; // 函数指针
// 打开动态链接库
handle = dlopen("/home/test/libSKFInterface.so", RTLD_LAZY);
if (!handle)
{
fprintf(stderr, "Error: %s\n", dlerror());
return 1;
}
// 获取动态链接库中的函数指针
pfnSKF_ConnectDev = (PFN_SKF_ConnectDev)dlsym(handle, "SKF_ConnectDev");
if (!pfnSKF_ConnectDev)
{
fprintf(stderr, "Error: %s\n", dlerror());
dlclose(handle);
return 1;
}
unsigned long result = pfnSKF_ConnectDev("devName", &hDev);
if (0 == result)
{
printf("Connect successful. \n");
}
else
{
printf("Connect failed. Error code: %lx \n", result);
}
// 关闭动态链接库
dlclose(handle);
return 0;
}
错误信息展示:
//如果在该路径下没找到libSKFInterface.so
Error: /home/test/libSKFInterface.so: cannot open shared object file: No such file or directory
//如果libSKFInterface.so 中没找到SKF_ConnectDev
Error: /home/test/libSKFInterface.so: undefined symbol: SKF_ConnectDev
如果.so在当前路径,但是dlopen无法从你写的路径获取到.so, 用readlink读取/proc/self/exe可以获取当前程序的绝对路径。(/proc/self/exe 是一个符号链接,指向当前进程的可执行文件)
6.readlink
头文件: #include <dlfcn.h>
函数定义:ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
功能: 读取符号链接 path 指向的内容到缓冲区 buf 中。
参数:
pathname: 要读取的符号链接的路径名。
buf: 用于存放符号链接目标或符号链接本身的缓冲区。
bufsiz: 缓冲区的大小,即能够存放的最大字节数。
返回: 放入 buf 的字节数。
错误时返回 -1。
代码举例说明:
#include <stdio.h>
#include <dlfcn.h> // 包含动态链接库相关的头文件
#include <unistd.h>
#include <string.h>
char *getSoPath(char *buf, int count)
{
int rslt = readlink((char*)"/proc/self/exe", buf, count - 1);
if (rslt < 0 || (rslt >= count - 1))
{
return NULL;
}
buf[rslt] = '\0';
printf("LINE = %d, buf = %s\n",__LINE__, buf);
return buf;
}
int main()
{
void *handle; // 动态链接库句柄
char *p = NULL;
char soPath[128] = { 0 };
memset(soPath, 0x00, sizeof(soPath));
getSoPath(soPath, sizeof(soPath));
p = strrchr(soPath, '/');
if(p != NULL)
{
strcpy(p + 1, "libSKFInterface.so");
}
printf("LINE = %d, soPath = %s\n",__LINE__, soPath);
//打开动态链接库
handle = dlopen(soPath, RTLD_LAZY);
if (!handle)
{
fprintf(stderr, "Error: %s\n", dlerror());
return 1;
}
return 0;
}