动态库
概述
动态库的扩展名是.so。
动态库是被加载,调用的时候是根据内存地址去调用,而不是将代码复制到文件中。
动态库可以同时被多个进程使用。
实战案例:构建 libmath.so 动态库
准备源文件
calc.h
- 定义加法:
int add(int a, int b);
- 定义减法:
int sub(int a, int b);
#ifndef __CALC_H_
#define __CALC_H_
int add(int a, int b);
int sub(int a, int b);
#endif // __CALC_H_
calc.c
- 简单的实现加法
- 简单的实现减法
#include "calc.h"
int add(int a, int b){
return a + b;
}
int sub(int a, int b){
return a - b;
}
show.h
#ifndef __SHOW_H_
#define __SHOW_H_
void show(int a, char* op, int b, int res);
#endif // __SHOW_H_
show.c
#include <stdio.h>
#include "show.h"
void show(int a, char* op, int b, int res){
printf("%d %s %d = %d\n", a, op, b, res);
}
编译C源文件
gcc -c -fpic calc.c
gcc -c -fpic show.c
构建动态库
gcc -shared calc.o show.o -o libmath.so
使用动态库
main.c
#include <stdio.h>
#include "calc.h"
#include "show.h"
int main(){
int a = 11;
int b = 22;
int res = add(a, b);
show(a, "+", b, res);
return 0;
}
编译并运行文件,此时把静态库文件也带上:
# 先配置库所在的环境变量
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
# 编译执行
gcc main.c libmath.so -o main && ./main
输出结果如下:
11 + 22 = 33
动态加载常用方法
dlopen 方法
语法:
void* dlopen(char const** filename, int flag)
功能:将动态共享库载入内存并获得其访问句柄。
参数:
- filename:动态库路径,若只给文件名不带目录,则根据LD_LIBRARY_PATH环境变量的值搜索动态库
- flag:
- RTLD_LAZY:延迟加载,使用动态库中的符号时才真的加载到内存
- RTLD_NOW:立即加载
返回值:成功返回动态库的访问句柄,失败返回NULL。
句柄:唯一标识了系统内核所维护的动态共享库对象,将作为后续函数调用的参数。
dlclose 方法
语法:
int dlclose(void * handle)
功能:从内存中卸载动态库
参数:handle,动态句柄
返回值:成功返回0,失败返回非0
所卸载的共享库未必真的会立即从内存中消失,因为其他程序可能还需要使用该库。只有所有使用该库的程序都显式或者隐式的卸载了该库,该库所占用的内存空间才会真正得到释放。
无论卸载的共享库是否真正被释放,传递给close函数的句柄都会在该函数成功返回以后立即失效。
dlerror 方法
语法:
char* dlerror(void)
功能:获取在加载,使用和卸载共享库过程中所发生的错误。
返回值:有错误则返回指向错误信息字符串的指针,否则返回NULL。
使用示例:
void* handle = dlopen("libmath.so", RTLD_NOW);
if (!handle){
fprintf(stderr, "dlopen: %s\n", dlerror());
exit(EXIT_FAILURE);
}
dlsym 方法
语法:
void* dlsym(void* handle, char const* symbol)
功能:从已被加载的动态库中获取特定名称的符号地址
参数:
- handle 动态库访问句柄
- symbol 符号名
返回值:成功返回给定符号的地址,失败返回NULL
该函数 所返回的指针为void*类型,需要转型为实际目标类型相一致的指针后才能使用。
实战:动态加载 libmath.so 动态库
准备 .so 文件:
mkdir lib
mv libmath.so ./lib/
ls lib
编写main.c
#include <stdio.h>
#include <dlfcn.h>
int main(){
// get the .so handle
void* handle = dlopen("./lib/libmath.so", RTLD_NOW);
if(handle == NULL){
fprintf(stderr, "dlopen error: %s\n", dlerror());
return -1;
}
// get the func address
int (*add)(int, int) = dlsym(handle, "add");
if(add == NULL){
fprintf(stderr, "dlsym error: %s\n", dlerror());
return -1;
}
// use the add func
int value = add(11, 22);
printf("11 + 22 = %d\n", value);
// close the handle
if(dlclose(handle)){
fprintf(stderr, "dlclose error: %s\n",dlerror());
return -1;
}
return 0;
}
编译并执行程序:
gcc -o main main.c -ldl && ./main
输出:
11 + 22 = 33