动态和静态链接库
Posted reghao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态和静态链接库相关的知识,希望对你有一定的参考价值。
函数库是现有的、可复用的代码。从本质上讲,库是一种可执行代码的二进制形式,可被操作系统载入内存执行。实际上,库就是一些头文件(.h)和库文件(.a、.so 或 .lib、.dll)的集合。Linux 将头文件和库文件分别放在 /usr/include/ 和 /usr/lib/ 目录。在某些时候程序需要的库不在这些目录下,此时需要在编译时指定所需的头文件和库文件的路径。
函数库分为静态链接库 (.a、.lib) 和动态链接库 (.so、.dll) 两种。所谓静态、动态是指链接时的状态,它们的区别在于“链接阶段”如何处理库,以链接生成可执行程序。
.a 和 .so 后缀名表示 Linux 中的静态库和动态库。
.lib 和 .dll 后缀名表示 Windows 中的静态库和动态库,
静态库的名字一般为 libxxx.a,其中 xxx 是此库的名称。
动态库的名字一般为 libxxx.so.major.minor,xxx 是该库的名称,major 是主版本号, minor 是副版本号。
gcc 在链接阶段优先使用动态链接库,只有动态链接库不存在时,才会使用静态链接库。
使用 -static 选项编译时会强制使用静态链接库。
实验文件
hello.h hello 函数的定义文件
hello.c hello 函数的实现文件
main.c 主程序
hello.h
#include <stdio.h>
void hello();
hello.c
#include "hello.h"
void hello() {
printf("hello world
");
}
main.c
#include "hello.c"
int main(int argc, char *argv[]) {
hello();
}
静态链接库
静态库在链接阶段会将汇编生成的目标文件(.o 文件)与引用到的库一起链接打包生成可执行文件,这种链接方式即静态链接,此时生成的可执行程序的体积较大。由于静态库与汇编生成的目标文件一起链接成为可执行文件,因此静态库和 .o 文件相似。
实际上,静态库是一组目标文件 (.o/.obj 文件) 的集合,即多个目标文件经过打包压缩后生成的一个文件。
静态库的后缀是 .a,它的产生分为两步:
- 由源文件编译生成一堆 .o 文件,每个文件都包含这个编译单元的符号表。
- ar 命令将一个或多个 .o 文件转换成一个 .a 文件,即成为静态库。
生成 hello.o 文件
$ gcc -c hello.c
生成静态库文件 libhello.a
$ ar rcs libhello.a hello.o
生成可执行程序 main
$ gcc -o main main.c -L. -lhello
- -c 选项:生成 obj 文件
- -L 选项:指定编译时搜索的路径
- -l 选项:指定编译时使用的库
静态库在链接时的路径搜索顺序:
- ld 首先会去查找 gcc 命令中的参数 -L
- 然后查找 gcc 的环境变量 LIBRARY_PATH 指定的路径
- 然后再查找系统指定目录 /lib、/usr/lib 以及 /usr/local/lib(这在编译时由 gcc 写到程序内)。
静态库的特点:
便于移植
由于静态库在链接阶段已被加入到可执行文件中,因此程序运行时不再需要该静态库,这使得程序的移植很方便。
浪费空间和资源
所有相关的目标文件与涉及的函数都被链接到了可执行文件,因此可执行文件的体积较大。此外,若运行了很多程序,且这些程序都使用了同一个库函数,那么在内存中会大量拷贝这个库函数,这造成了内存和存储空间的浪费
动态链接库
动态库在编译链接阶段只将对它的引用链接到目标代码中,在程序运行时,最终的函数导入内存开始执行,函数引用被解析,此时共享库代码才被载入内存。这使得生成的可执行文件体积较小。
若不同的应用程序调用相同的共享库,那么在内存里只需维持一份该共享库的实例即可。这使得内存空间的使用大大减少。共享函数库的另一个优点是,它可以独立更新,不会影响调用它的函数。
动态库的后缀是 .so,它由 gcc 加特定参数编译产生。系统加载可执行代码时候,可自动知道其所依赖库的名字,但是并不知道库的绝对路径,此时就需要系统动态载入器 (dynamic linker/loader)。
对于 elf 格式的可执行程序,路径查找是由 ld-linux.so 来完成,它依次搜索 elf 文件的:
- 编译目标代码时指定的动态库搜索路径(DT_RPATH 段)
- LD_LIBRARY_PATH 环境变量
- /etc/ld.so.cache 文件
- /lib/ 目录
- /usr/lib 目录
找到库文件后将其载入内存。
新安装的库若在 /lib 或者 /usr/lib 下,那 ld 默认能够找到。若在其他目录,则需添加到 /etc/ld.so.cache 文件:
- 编辑 /etc/ld.so.conf 文件,加入库文件所在目录的路径
- 运行 ldconfig,该命令会重建 /etc/ld.so.cache 文件
使用 ldd 命令可以查看一个可执行程序依赖的共享库。
生成动态库文件 libhello.so
$ gcc -fPIC -c hello.c
$ gcc -shared -o libhello.so hello.o
生成可执行文件 main
$ gcc -o main main.c -L. -lhello
此时若执行 main 程序会报错
$ ./main
./main: error while loading shared libraries: libdyn.so: cannot open shared object file: No such file or directory
一种方法是将环境变量 LD_LIBRARY_PATH 设置为当前目录(程序所在的目录)。
$ export LD_LIBRARY_PATH=$(pwd)
另一种方法是将 libhello.so 文件放到 /usr/lib,目录,并使用 ldconfig 命令重建 /etc/ld.so.cache 文件(非必须)。
$ cp libhello.so /usr/lib
$ ldconfig /usr/lib
区别
动态库和静态库的区别:
- 在链接阶段,采用何种方式与汇编产生的目标文件一起生成可执行文件
- 函数库与目标文件一起生成可执行文件
- 函数库的引用于目标文件一起生成可执行文件
动态库的优缺点:
- 占用的空间和资源小,多个程序可使用一个共享库实例
- 移植性差,程序运行时需要动态库
静态库的优缺点:
- 占用空间和资源大,每个程序都需要独自的静态库实例
- 移植性好,可执行文件生成后就不再需要静态库
以上是关于动态和静态链接库的主要内容,如果未能解决你的问题,请参考以下文章