一、什么是动态库?
1、动态库和静态库不同,链接动态库不需要将被调用的函数代码复制到包含调用代码的可执行文件中,相反链接器会在调用语句处嵌入一段指令,在该程序执行到这段指令时,会加载该动态库并寻找被调用函数的入口地址并执行之。
2、如果动态库中的代码同时为多个进程所用,动态库在内存的实例仅需一份,为所有使用该库的进程所共享,因此动态库亦称共享库。
3、动态库的拓展名是 .so 例libxxx.so。
二、动态库的构建顺序以及编译动态库
A. 编辑库的实现代码和接口声明
– 计算模块:calc.h、calc.c
– 显示模块:show.h、show.c
– 接口文件:math.h
B. 编译成目标文件
gcc -c -fpic calc.c
gcc -c -fpic show.c
C. 打包成动态库
gcc -shared calc.o show.o -o libmath.so
编译链接也可以合并成一步完成
gcc -shared -fpic calc.c show.c -o libmath.so
编辑库的使用代码
main.c
编译并链接动态库
直接链接动态库
gcc main.c libmath.so
用-l指定库名,用-L指定库路径
gcc mian.c -lmath -L.
用-l指定库名,用LIBRARY_PATH环境变量指定库路径
export LIBRARY_PATH=$LIBRARY_PATH:.
gcc main.c -lmath
三、动态库的动态加载和卸载动态库
函数以及功能
void* dlopen(char const* filename, int flag);
功能:将共享库载入内存并获得其访问句柄
参数:filename 动态库路径,若只给文件名不带目录,则根据LD_LIBRARY_PATH环境变量的值搜索动态库
flag 加载方式,可取以下值:
RTLD_LAZY - 延迟加载,使用动态库中的符号时才真的加载进内存。
RTLD_NOW - 立即加载。
返回值:成功返回动态库的访问句柄,失败返回NULL。
句柄:句柄唯一地标识了系统内核所维护的共享库对象,将作为或许函数调用的参数
int dlclose(void* handle);
功能:从内存中卸载动态库
参数:handle 动态库句柄
返回值:成功返回0,失败返回非0。
所卸载的共享库未必会真的从内存中立即消失,因为其他程序可能还需要使用该库
只有所有使用该库的程序都显示或隐式地卸载了该库,该库所占用的内存空间才会真正得到释放
无论所卸载的共享库是否真正被释放,传递给dlclose函数的句柄都会在该函数成功返回后立即失效
load.c文件
1 #include<stdio.h>
2 #include<dlfcn.h>
3
4 int main(){
5
6 //加载动态库
7 void* handle = dlopen("/home/tarena/day02/shared/libmath.so",RTLD_NOW);
8 if(handle == NULL){
9 fprintf(stderr,"dlopen:%s\n",dlerror());
10 return -1;
11 }
12 //使用动态库
13 int (*p_add)(int,int) = (int (*)(int,int))dlsym(handle,"add");
14 if(p_add == NULL){
15 fprintf(stderr,"dlsym:%s\n",dlerror());
16 return -1;
17 }
18
19 int (*p_sub)(int,int) = (int(*)(int,int))dlsym(handle,"sub");
20
21 if(p_sub == NULL){
22 fprintf(stderr,"dlsym:%s\n",dlerror());
23 return -1;
24 }
25 int (*p_show)(int,char,int,char,int) = (int(*)(int,char,int,char,int))dlsym(h andle,"show");
26 if(!p_show){
27 fprintf(stderr,"dlsym:%s\n",dlerror());
28 }
29
30 int a=3,b=4;
31 p_show(a,'+',b,'=',p_add(a,b));
32 //卸载动态库
33 if(dlclose(handle)){
34 fprintf(stderr,"dlclose:%s\n",dlerror());
35 return -1;
36 }
37
38 return 0;
39 }
构建可执行程序
gcc load.c -o load
运行程序./load