动/静态库的原理及制作
一.基本概念
动态库:动态库就像一个仓库其中包含为可执行文件,如果有文件需要调用,就进行提取,使用动态库编译的文件因为保存的地址所以文件大小相对较小,依赖系统环境。
静态库:相当于2进制的.c文件,用来编译在文件内部,并且有相对较强的可移植性,若文件采用静态库进行编译,不依赖系统环境,但是文件体积相对较大。
二.动态库的制作
(1)将.c源文件编译为.o文件
制作动态库所需要的为有段落行的二进制文件编译时需要加特定的命令
gcc main.c -o main.o -c -fPIC
-c :GCC 会将指定的源代码文件(如 .c
或 .cpp
文件)编译成目标文件(通常为 .o
或 .obj
文件),但不会尝试将它们链接成可执行文件。这意味着编译器不会处理外部引用,只关注于源代码内部的编译。
-fPIC:GCC 会生成位置无关代码,这意味着代码中的所有地址都是相对于某个基址
(2)动态库可以当作共享的.so文件
gcc main.o -shared -o libmain.so
-shared:用于生成共享库(Shared Library,也称为动态链接库,以便可以被多个程序共享使用。
其中有一些潜规则在进行连接"-l"的时候其中库的名称时掐头去尾 例如libmain.so其实时main
(3)运行
<1>最简单直接的方法:将生成的.so文件移送到系统的lib文件夹下 编译命令如下
gcc main.c -o main -L <系统lib的路径一般时/lib> -l <库的名称>
<2>给链接库知名路径
gcc main.c -o main -L. -lmain -Wl,-rpath=/home/gec/lib
-Wl
:在参数后的逗号分隔的参数会被直接传递给底层链接器
-rpath:用来给编译器指明所连接库的位置
<3>修改环境变量(只有在当前终端生效)
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/gec/lib
<4>修改系统的环境变量
gec@ubuntu:~$ sudo vi /etc/ld.so.conf.d/libc.conf
gec@ubuntu:~$ sudo ldconfig
三.静态库的制作
假设功能文件 a.c、b.c
包含了一些通用的程序模块,可以被其他程序复用,那么可以将它们制作成静态库,具体的步骤是:
(1)制作 *.o 原材料
gec@ubuntu:~$ gcc a.c -o a.o -c
gec@ubuntu:~$ gcc b.c -o b.o -c
(2)将 *.o 合并成一个静态库
gec@ubuntu:~$ ar crs libab.a a.o b.o
(3)静态库的常见操作
ar -<参数> 静态库名称
-t:意即table,以列表方式列出*.o文件
-d:d意即delte,删除掉指定的*.o文件
-r:r意即replace,添加或替换(重名时)指定的*.o文件
-x:x意即extract,将库中所有的*.o文件释放出来
(4)使用
gcc main.c -L /path/to/libmain.a -l main -o main
其中路径一定为绝对路径
四.动态加载库
1.接口代码实现
// a.c
void detection()
{printf("正在检测颜色是否均匀...\n");
}// b.c
void detection()
{printf("正在检测外观是否破损...\n");
}
2.将不同的模块制作成动态库
gec@ubuntu:~$ gcc a.c -o a.o -c -fPIC
gec@ubuntu:~$ gcc -shared -fPIC -o libcolor.so a.o
gec@ubuntu:~$
gec@ubuntu:~$ gcc b.c -o b.o -c -fPIC
gec@ubuntu:~$ gcc -shared -fPIC -o libshape.so b.o
gec@ubuntu:~$
gec@ubuntu:~$ ls
libcolor.so libshape.so
3.编写一个配置文件,指定程序需要加载的动态库:
gec@ubuntu:~$ cat config
libcolor.so
示例代码
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <errno.h>int main(int argc, char **argv)
{// 读取配置文件FILE *fp = fopen("config", "r");char *lib = calloc(1, 30);fgets(lib, 30, fp);fclose(fp);// 根据配置文件打开指定的库void *handle = dlopen(strtok(lib, "\n"), RTLD_NOW);if(handle == NULL){printf("加载动态库[%s]失败:%s\n", lib, strerror(errno));exit(0);}// 在库中查找事先约定好的接口void (*detect)(void);detect = dlsym(handle, "detection");if(detect == NULL){printf("查找符号[%s]失败:%s\n", "detect", strerror(errno));exit(0);}// 潇洒地调用该接口detect();
}
五.函数解析
打开和关闭动态库,获取动态库的操作句柄:
关键点:
- RTLD_LAZY意味着打开动态库时,并不立即解析库中的函数符号的内存位置,而是等待程序实际调用时才临时去解析。
- RTLD_NOW与上述含义相反,它意味着打开动态库时就立即解析库中的函数符号的内存位置。
- 不管是LAZY还是NOW,库中的静态数据符号都将被立即解析。
关键点:
- 该函数用于在动态库中获取指定的函数入口地址。