前言
我们在编写代码的时候经常用到已有的接口,他们是以库的形式提供给我们使用的,而常见形式有两种,一种常以 .a 为后缀,为静态库;另一种以 .so 为后缀,为动态库。所谓的静态、动态指的是链接的过程。
一、静态库和动态库介绍
1、静态库
程序在编译链接的时候把库的代码链接到可执行文件中,程序运行的时候将不再需要静态库。
静态库在文件中静态展开,所以有多少文件就展开多少次,非常吃内存,100M 展开 100 次,就是 10G,但是这样的好处就是静态加载的速度快。
2、动态库
程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
使用动态库会将动态库加载到内存,10 个文件也只需要加载一次,然后这些文件用到库的时候临时去加载,速度慢一些,但是很省内存。
二、静态库的制作及使用
静态库的名字是以 lib 开头,以 .a 结尾,例:libmylib.a
静态库生成的指令:
ar rcs libmylib.a file1.o
1、准备好源码
test.c
#include <stdio.h> #include "sub.h" int main(int argc, char *argv[]) { int i = 6, j = 3; int k; printf("main fun! && i = %d\n", i); k = sub_fun(i, j); printf("k = %d\n", k); return 0; }
sub.c
#include <stdio.h> int sub_fun(int a, int b) { printf("sub fun!\n"); return a - b; }
sub.h
int sub_fun(int, int);
2、编译源码生成 .o 文件
gcc -c sub.c -o sub.o
3、制作静态库
ar rcs libmymath.a sub.o
4、使用静态库
gcc test.c libmymath.a -o test ./test
test.c 文件仅有 213 字节,而 test 文件有 16K 大小,所以静态库使用时,是直接编译到文件里面的。
三、动态库的制作及使用
动态库的名字是以 lib 开头,以 .so 结尾,例:libmylib.so
1、生成位置无关的 .o 文件
gcc -c sub.c -o sub.o -fPIC
使用 -fPIC 这个参数过后,生成的函数就和位置无关,挂上 @plt 标识,等待动态绑定
2、制作动态库
gcc -shared -o libmymath.so sub.o
3、使用动态库
- -l :指定库名
- -L :指定库路径
gcc test.c -o test -l mymath -L ./ ./test
出错原因分析:
- 连接器: 工作于链接阶段,工作时需要 -l 和 -L
- 动态链接器: 工作于程序运行阶段,工作时需要提供动态库所在目录位置
解决办法:指定动态库路径并使其生效,然后再执行文件
4、指定动态库路径并使其生效
通过环境变量指定动态库所在位置: export LD_LIBRARY_PATH=动态库路径
export LD_LIBRARY_PATH=./ ./test
当关闭终端,再次执行 test 时,又报错。
这是因为,环境变量是进程的概念,关闭终端之后再打开,是两个进程,环境变量发生了变化。
要想永久生效,需要修改 bash 的配置文件: vi ~./bashrc
永久生效方法:
- vi ~/.bashrc
- 写入 export LD_LIBRARY_PATH=动态库路径 保存
- source ~/.bashrc,再重启终端 —> 让修改后的 .bashrc 生效
- 执行 ./test
四、对比
1、静态库优缺点
- 静态库的加载速度快
- 发布程序无需提供静态库,移植方便
- 消耗系统资源,浪费内存
- 更新、部署和发布麻烦
2、动态库优缺点
- 可以实现进程间的资源共享
- 更新、部署、发布简单
- 可以控制何时加载动态库
- 加载速度比较慢
- 发布程序时需要提供依赖的动态库