静态库和动态库详解
1.什么是库
库文件是计算机上的一类文件,可以简单的把库文件看成一种代码仓库,它提供给使用者一些可以直接拿来用的变量、函数或类。
库是特殊的一种程序,编写库的程序和编写一般的程序区别不大,只是库不能单独运行。
库文件有两种,静态库和动态库(共享库),区别是:
- 静态库在程序的链接阶段被复制到了程序中;
- 动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。
库的好处:
- 代码保密
- 方便部署和分发
2.静态库的制作
静态库的命名规则:
- Linux:
libxxx.a
- Windows:
libxxx.lib
静态库的制作:
gcc
获得 .o 文件- 使用
ar
工具(archive)将 .o 文件打包r
- 将文件插入备存文件中c
- 建立备存文件s
- 索引
//add.c, sub.c, mult.c, div.c
gcc -c add.c #获得add.o, sub.o, mult.o, div.o
ar rcs libcalc.a add.o, sub.o, mult.o, div.o # 获得静态库libxxx.a
静态库的使用
链接时须注意头文件的展开和库文件的加载,另外还需注意目录层级。
$ tree
.
├── include
│ └── head.h
├── lib
│ └── libcalc.a
├── main.c
└── src
├── add.c
├── div.c
├── mult.c
└── sub.c
错误的做法:
正确的做法:
# -I指定头目录,-l指定库名称,-L指定库路径
$ gcc main.c -o app -Iinclude -lcalc -Llib
$ ./app #正确运行
a = 20, b = 12
a + b = 32
a - b = 8
a * b = 240
a / b = 1.666667
3.动态库的制作
动态库的命名规则
- Linux:
libxxx.so
(Linux下是一个可执行文件) - Windows:
libxxx.dll
动态库的制作
gcc
获得.o
文件,得到和位置无关的代码gcc
获得动态库
-fpic
用于汇编阶段,产生的目标文件没有绝对地址,全部用相对地址,这正好满足了共享库的要求,共享库被加载时地址不是固定的。如果不加 -fpic
,那么生成的目标文件就会与位置有关。
$ gcc -c -fpic add.c sub.c div.c mult.c //生成.o文件
$ gcc -shared *.o -o libcalc.so
使用动态库
$ gcc main.c -o dynamic_app -Iinclude -L../calc -lcalc
$ ./dynamic_app
a = 20, b = 12
a + b = 32
a - b = 8
a * b = 240
a / b = 1.666667
TODO:实测加不加
-fpic
没什么区别,都是位置无关。
4.静态库和动态库的工作原理
- 静态库:GCC 进行链接时,会把静态库中代码打包到可执行程序中
- 动态库:GCC 进行链接时,动态库的代码不会被打包到可执行程序中
- 程序启动之后,动态库会被动态加载到内存中,通过
ldd
(list dynamic dependencies)命令检查动态库依赖关系 - 如何定位共享库文件呢?
- 当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路 径。此时就需要系统的动态载入器来获取该绝对路径。对于elf格式的可执行程序,是 由
ld-linux.so
来完成的,它先后搜索elf文件的DT_RPATH
段 ——> 环境变量LD_LIBRARY_PATH
——>/etc/ld.so.cache
文件列表 ——>/lib/
,/usr/lib
目录找到库文件后将其载入内存。
- 当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路 径。此时就需要系统的动态载入器来获取该绝对路径。对于elf格式的可执行程序,是 由
如何配置共享库文件?
- 临时配置(切换shell失效)
# 处理生成app文件(app非必定名字)
gcc main.c -o app -I ./include/ -l calculate -L ./lib/
# 配置环境变量,值为lib文件的绝对路径(lib文件夹下用pwd指令输出绝对路径)
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:lib文件的绝对路径
# 在处理后生成的app文件下用lld指令,如果libcalc.so已分配内存且路径在lib文件夹下则链接成功
ldd app
- 永久配置1(用户级)
# 处理生成app文件(app非必定名字)
gcc main.c -o app -I ./include/ -l calculate -L ./lib/
# 切入home/{current_user}文件下找到.bashrc文件修改
vim ~/.bashrc
# 在.bashrc文件最后一行插入以下指令
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:lib文件的绝对路径
# 生效更新
. .bashrc # source .bashrc
# 在处理后生成的app文件下用lld指令,如果libcalc.so已分配内存且路径在lib文件夹下则链接成功
ldd app
- 永久配置2(系统级)
# 处理生成app文件(app非必定名字)
gcc main.c -o app -I ./include/ -l calculate -L ./lib/
# 用管理员身份进入系统变量设置文件
sudo vim /etc/profile
# 在profile文件最后一行插入以下指令
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:lib文件的绝对路径
# 生效更新
. /etc/profile # source /etc/profile
# 在处理后生成的app文件下用lld指令,如果libcalc.so已分配内存且路径在lib文件夹下则链接成功
ldd app
- 永久配置3(系统级,便捷)
# 处理生成app文件(app非必定名字)
gcc main.c -o app -I ./include/ -l calculate -L ./lib/
# 由于/etc/ld.so.cache文件是二进制文件,所以我们间接修改/etc/ld.so.conf文件:插入lib文件的绝对路径保存即可
sudo vim /etc/ld.so.conf
# 生效更新
sudo ldconfig
# 在处理后生成的app文件下用lld指令,如果libcalc.so已分配内存且路径在lib文件夹下则链接成功
ldd app
5.静态库和动态库的区别
静态库、动态库区别来自链接阶段如何处理链接成可执行程序。分为静态链接方式和动态链接方式。
静态库优缺点
-
优点
- 静态库被打包到应用程序中加载速度快。
- 发布程序无需额外提供静态库,移殖方便。
-
缺点
-
消耗系统资源,浪费内存。
-
更新、部署、发布麻烦。
动态库优缺点
- 优点
- 可以实现进程间资源共享(共享库)。
- 更新、部署、发布简单。
- 加载动态库时间可控。
- 缺点
- 加载速度相对于静态库慢。
- 发布程序时需要提供依赖的动态库。