linux环境下,调用so动态链接库,有两种方式,显示调用(dlopen加载so库,dlsym获取函数指针)和隐式调用(编译时需要头文件和so库)。本文主要讲隐式调用。
隐式调用由系统加载完成,对程序员透明;显式调用由程序员在需要使用时自己加载,不再使用时,自己负责卸载
(1)动态库代码:dl_func.c
extern char name[];
int add(int a, int b)
{
printf("calling add\n");
printf("Hello, %s!\n", name);
return a + b;
}
编译so动态库:
gcc -o libdl_func.so -fPIC -rdynamic -shared dl_func.c
选项-fPIC指示编译器将代码编译成位置独立的代码,一般需要以程序文件共享其函数或变量给其他程序文件的代码都应该以此选项进行编译,选项-rdynamic指示编译器所编译/链接的为共享库程序文件。由于要使用外部变量,因此需要-shared选项,否则编译器会抛出错误信息:undefined reference to `name',表示不能找到name变量。
(2)隐式调用so代码:dl_demo1.c
int add(int a, int b);
char name[100];
int main(int argc, char *argv[]) {
int a = 10, b = 20;
int c = 0;
strcpy(name, "NHN XDBMS");
c = add(a, b);
printf("%d + %d = %d\n", a, b, c);
return 0;
}
编译主程序:
gcc -o dl_demo1 -L./ -ldl_func dl_demo1.c
选项-L./ 指示编译器在当前目录下寻找共享库文件,-ldl_func指示需要的共享库文件名为libdl_func.so。
运行:./dl_demo1
踩坑:cannot open shared object file: No such file or directory
可通过ldd 库 查看
(3)显示调用so代码:dl_demo.c
char name[100];
int main(int argc, char *argv[]) {
int a = 10, b = 20;
int c = 0;
void *dlh = NULL;
int (*add)(); //定义函数指针
strcpy(name, "NHN XDBMS");
if((dlh = dlopen("libdl_func.so", RTLD_LAZY)) == NULL) {
fprintf (stderr, "***DL ERROR: %s.\n", dlerror ());
return 1;
}
if((add = (int (*)())dlsym(dlh, "add")) == NULL) {
fprintf (stderr, "***DL ERROR: %s.\n", dlerror ());
return 1;
}
c = add(a, b);
printf("%d + %d = %d\n", a, b, c);
dlclose(dlh);
return 0;
}
编译主程序:
gcc -o dl_demo -fPIC -ldl dl_demo.c
由于变量name需要被共享库中的add()函数使用,因此必须使用选项-fPIC。选项-ldl指示编译器需要用来到libdl.so库文件。
显示调用必须定义函数指针,运行时加载dll库到内存,获取so库中函数的指针赋值给预先定义的函数指针,最好使用函数指针调用。过程比隐式调用繁琐。
1)void *dlopen(const char *file, int mode);
该函数用来按照指定模式打开指定的共享库,将其影射到内存中,并且返回句柄。
第一个参数:指定共享库的名称,将会在下面位置查找指定的共享库。
-环境变量LD_LIBRARY_PATH列出的用分号间隔的所有目录。
-文件/etc/ld.so.cache中找到的库的列表,用ldconfig维护。
-目录usr/lib。
-目录/lib。
-当前目录。
第二个参数:指定如何打开共享库。
-RTLD_NOW:将共享库中的所有函数加载到内存
-RTLD_LAZY:会推后共享库中的函数的加载操作,直到调用dlsym()时方加载某函数
2)void *dlsym(void *restrict handle, const char *restrict name);
该函数返回一个指向由name所确定的请求入口点的指针。调用dlsym时,利用dlopen()返回的共享库的phandle以及函数/变量名称作为参数,返回要加载函数/变量的入口地址。