1、什么是库
库文件是计算机上的一类文件,提供给使用者一些开箱即用的变量、函数或类。静态库和动态库,静态库和动态库的区别体现在程序的链接阶段:静态库在程序的链接阶段被复制到了程序中;动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。
假设当前目录下有以下文件
├── main.c
├── tools.c
└── tools.h
tools.h 包含get_sum函数的声明
#ifndef tools_h
#define tools_h
int get_sum(int a,int b);
#endif /* tools_h */
tools.c 包含get_sum函数的定义
int get_sum(int a, int b){
return a+b;
}
main.c 需要使用到get_sum函数
#include <stdio.h>
#include "tools.h"
int main(){
printf("%d\n",get_sum(12,12));
return 0;
}
2、静态库
在程序的链接阶段被复制到了程序中,编译完后就不再需要引入的库了。
2.1 命名规则
在Linux平台下:libxxx.a
libxxx.a:库文件的名字
lib:前缀(固定)
xxx:库的名字,自己起
.a:后缀(固定)
windows:libxxx.lib
2.2 制作方法
tools.c
int get_sum(int a, int b){
return a+b;
}
tools.h
#ifndef tools_h
#define tools_h
int get_sum(int a,int b);
#endif /* tools_h */
上述源文件包含一个求和函数,现在我们想把它打包成一个库供其他程序使用。
-
获得.o文件
使用-c参数,只生成汇编程序,不进行链接
gcc -c tools.c
-
将.o文件打包,使用ar工具(archive)
ar rcs libtools.a tools.o
r:将文件插入备存文件中,即libtools.a
c:create,创建备存文件
s:缩引
2.3 使用方法
此时文件结构:
├── libtools.a
├── main.c
├── tools.c
├── tools.h
└── tools.o
为了测试调用库中的文件,现在删除tools.c,tools.o,但不能删除tools.h,使用静态库来编译程序的时候需要用到,tools.h中保存了库文件中函数的定义。
现在目录结构:
├── libtools.a
├── main.c
└── tools.h
现在使用库文件libtools.a来编译main.c,需要指定-I(大i),-l -L参数
-I(大i,Include):指定头文件路径
-l :库的名字
-L:库文件的路径
$ gcc main.c -o app -I ./ -l tools -L ./
3、动态库
3.1 命名规则
Linux:libxxx.so
lib:前缀(固定)
xxx:库的名字
.so:后缀(固定)
在Linux下是一个可执行文件
windows:libxxx.dll
3.2 制作方法
假设此时目录结构:
├── main.c
├── tools.c
└── tools.h
内容和1、一致
-
获得和位置无关的.o文件,需要添加 -fpic参数,在某些平台可能需要-fPIC
$ gcc -c -fpic tools.c
生成tools.o
-
获得动态库
$ gcc -shared tools.o -o libtools.so
现在动态库就制作好了,此时目录结构:
├── libtools.so
├── main.c
├── tools.c
├── tools.h
└── tools.o
3.3 使用方法
-
编译main.c
-I(大i,Include):指定头文件路径
-l :库的名字
-L:库文件的路径
$ gcc main.c -o app -I ./ -l tools -L ./
-
运行app
$ ./main$ ./app./app: error while loading shared libraries: libtools.so: cannot open shared object file: No such file or directory
此时报错了,原因是没有找到动态库,此时需要手动置顶动态库的地址。
-
ldd,该命令可以查看一个程序的依赖关系,即需要导入哪些动态库
$ ldd ./app linux-vdso.so.1 (0x00007ffdab47f000) libtools.so => not found libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd4861ad000) /lib64/ld-linux-x86-64.so.2 (0x00007fd4867a0000)
可以看到,运行该程序需要libtools.so,此时显示未找到。
-
指定动态库路径
假设动态库所在路径为/home/so
临时指定,只在当前终端有效,关闭了或其他终端都不可用。
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/so
长期指定
# 将上述命令写入用户的配置文件.bashrc,这样每次打开新的终端都会执行上面的代码vim ~/home/.bashrc# 在打开的文件最后写入 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/so# 保存退出
-
再次运行app
$ ./app24$ ldd app$ ldd ./app # 查看app的依赖 linux-vdso.so.1 (0x00007fff4c1b5000) libtools.so => /home/so/libtools.so (0x00007f470e7fd000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f470e40c000) /lib64/ld-linux-x86-64.so.2 (0x00007f470ec01000)
4、原理
-
静态库:GCC进行链接时,将静态库中的代码打包到可执行程序中
-
动态库:GCC进行链接时,动态库的代码不会被打包到可执行程序中,只包含依赖
-
程序启动之后,动态库会被动态加载到内存中,通过ldd(list dynamic dependencies)命令检查动态库依赖关系。
-
如何定位共享库文件呢?
当系统加载可执行程序时,能够指导其所依赖的库的名字,但是还需要知道绝对路径,此时需要系统的动态载入器来获取动态库的绝对路径。
对于elf格式的可执行程序,有ld-linux.so来完成,它先后搜索DT_RPATH段,环境变量LD_LIBRARY_PATH,/etc/ld.so.cache文件列表,/lib/,/usr/lib目录找到库文件后将其载入内存。
5、区别
5.1 静态库
优点
- 静态库被打包到应用程序中加载速度快
- 发布程序无需提供静态库,移植方便
缺点
-
当多个程序需要使用用同一个库文件运行时,需要同时加载多个库文件,消耗系统资源,浪费内存。如下图所示:
-
更新,部署,发布麻烦
5.2 动态库
优点
-
可以实现进程间资源共享(共享库)
-
更新,部署,发布简单
-
可以控制何时加载动态库
缺点
加载速度比静态库慢
发布程序时,需要提供依赖的动态库