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