动态库和静态库
在 Linux 中,库文件提供了程序运行所需的各种功能,使得开发者能够高效地复用代码,减少重复劳动。库文件通常分为动态库(也称为共享库)和静态库两种类型,它们在程序链接和运行阶段扮演着不同的角色。
# 动态库和静态库
# 静态库
静态库 (Static Library) 是一种在编译时链接到程序中的库文件,它的后缀为.a(Linux 系统),静态库包含了程序运行所需的所有代码和数据,当程序编译时,链接器会将静态库中的代码和数据直接复制到生成的可执行文件中,生成的可执行程序在运行的时候将不再需要静态库,因此使用静态库生成的可执行程序的大小一般比较大。
- 优点:执行速度快: 由于代码已经包含在可执行文件中,不需要运行时动态链接,可以减少启动时间。即编译成功的可执行文件可以独立执行,而不需要再向外部要求读取函数库的内容。
- 缺点:使用静态库生成可执行程序会占用大量空间,特别是当有多个静态程序同时加载而这些静态程序使用的都是相同的库,这时在内存当中就会存在大量的重复代码;当静态库更新时,所有使用该库的程序都需要重新编译和链接。
# 动态库
动态库 (Dynamic Library) 是一种在程序运行时可以动态加载的库。它的后缀为.so (Shared Object) ,动态库也称为共享库。动态库是一种在程序运行时可以动态加载的库。这意味着动态库的内容(包括函数、变量和类等)并不在编译时被包含进程序本身,而是在程序运行时才根据需要被加载。因此,多个程序可以共享同一个动态库,从而节省内存空间。
在程序运行时,操作系统会将程序与动态库进行动态链接(外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中)。这种链接方式允许程序在运行时根据需要加载或卸载动态库,从而提高了程序的灵活性和可维护性。一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。
- 优点:节省磁盘空间,且多个用到相同动态库的程序同时运行时,库文件会通过进程地址空间进行共享,内存当中不会存在重复代码。
- 缺点:相对于静态链接的程序来说,动态链接的程序在启动时可能会有一定的延迟;动态链接的程序依赖于外部的动态库文件。如果这些库文件不存在、版本不匹配或路径不正确,程序将无法正常运行。
# 打包静态库
首先,我们简单写一个案例:
// add.cpp
#include "add.h"
void add(int a, int b) {
cout << a << " + " << b << " = " << a+b;
}
// --------------
// add.h
# pragma once
#include <iostream>
using namespace std;
extern void add(int a, int b);
// -------------
// test.cpp
#include "add.h"
int main() {
int a = 100;
int b = 20;
add(a, b);
cout << endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
执行 make 命令,编译生成可执行文件 test,执行./test,输出结果为:100 + 20 = 120
# 源文件生成目标文件
将源文件编译为目标文件。目标文件是包含机器代码但尚未链接的文件:
-rw-r--r-- 1 cen cen 136 Mar 20 20:25 add.cpp
-rw-r--r-- 1 cen cen 89 Mar 20 20:14 add.h
-rw-r--r-- 1 cen cen 78 Mar 20 20:26 Makefile
-rwxr-xr-x 1 cen cen 17056 Mar 20 20:27 test
-rw-r--r-- 1 cen cen 199 Mar 20 20:27 test.cpp
[cen@VM-4-9-opencloudos lesson02-动静态库]$ g++ -c add.cpp
[cen@VM-4-9-opencloudos lesson02-动静态库]$ ll
total 40
-rw-r--r-- 1 cen cen 136 Mar 20 20:25 add.cpp
-rw-r--r-- 1 cen cen 89 Mar 20 20:14 add.h
-rw-r--r-- 1 cen cen 2736 Mar 20 20:53 add.o
-rw-r--r-- 1 cen cen 78 Mar 20 20:26 Makefile
-rwxr-xr-x 1 cen cen 17056 Mar 20 20:27 test
-rw-r--r-- 1 cen cen 199 Mar 20 20:27 test.cpp
2
3
4
5
6
7
8
9
10
11
12
13
14
# ar 命令将目标文件生成静态库
ar 命令是 GNU 的归档工具,常用于将目标文件打包为静态库,下面我们使用 ar 命令的-r 选项和-c 选项进行打包
- -r(replace):若静态库文件当中的目标文件有更新,则用新的目标文件替换旧的目标文件。
- -c(create):建立静态库文件。
[cen@VM-4-9-opencloudos lesson02-动静态库]$ ar -rc libcal.a add.o
[cen@VM-4-9-opencloudos lesson02-动静态库]$ ll
total 44
-rw-r--r-- 1 cen cen 136 Mar 20 20:25 add.cpp
-rw-r--r-- 1 cen cen 89 Mar 20 20:14 add.h
-rw-r--r-- 1 cen cen 2736 Mar 20 20:53 add.o
-rw-r--r-- 1 cen cen 2882 Mar 20 20:55 libcal.a
-rw-r--r-- 1 cen cen 78 Mar 20 20:26 Makefile
-rwxr-xr-x 1 cen cen 17056 Mar 20 20:27 test
-rw-r--r-- 1 cen cen 199 Mar 20 20:27 test.cpp
2
3
4
5
6
7
8
9
10
# 头文件和静态库组织
当我们把自己的库给别人用的时候,实际上需要给别人两个文件夹,一个文件夹下面放的是一堆头文件的集合,另一个文件夹下面放的是所有的库文件。
因此,在这里我们可以将 add.h 头文件放到一个名为 include 的目录下,将生成的静态库文件 libcal.a 放到一个名为 lib 的目录下,然后将这两个目录都放到 mathlib 下,此时就可以将 mathlib 给别人使用了。
[cen@VM-4-9-opencloudos lesson02-动静态库]$ mkdir -p mathlib/include
[cen@VM-4-9-opencloudos lesson02-动静态库]$ mkdir -p mathlib/lib
[cen@VM-4-9-opencloudos lesson02-动静态库]$ cp ./*.h mathlib/include
[cen@VM-4-9-opencloudos lesson02-动静态库]$ cp ./*.a mathlib/lib
[cen@VM-4-9-opencloudos lesson02-动静态库]$ tree mathlib
mathlib
├── include
│ └── add.h
└── lib
└── libcal.a
2
3
4
5
6
7
8
9
10
# 使用静态库
[root@VM-4-9-opencloudos ~]# cp -r /home/cen/Linux/lesson02-动静态库/mathlib ./main
[root@VM-4-9-opencloudos ~]# ll
total 0
drwxr-xr-x 3 root root 21 Mar 20 21:57 main
[root@VM-4-9-opencloudos ~]# cd main
[root@VM-4-9-opencloudos main]# ll
total 0
drwxr-xr-x 4 root root 32 Mar 20 21:57 mathlib
[root@VM-4-9-opencloudos main]# touch main.cpp
[root@VM-4-9-opencloudos main]# vim main.cpp
[root@VM-4-9-opencloudos main]# ll
total 4
-rw-r--r-- 1 root root 100 Mar 20 21:58 main.cpp
drwxr-xr-x 4 root root 32 Mar 20 21:57 mathlib
[root@VM-4-9-opencloudos main]# g++ main.cpp -I./mathlib/include -L./mathlib/lib -lcal
[root@VM-4-9-opencloudos main]# ll
total 24
-rwxr-xr-x 1 root root 17056 Mar 20 21:59 a.out
-rw-r--r-- 1 root root 100 Mar 20 21:59 main.cpp
drwxr-xr-x 4 root root 32 Mar 20 21:57 mathlib
[root@VM-4-9-opencloudos main]# ./a.out
100 + 300 = 400
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
其中,-L 告诉编译器在哪个目录下搜索库文件,-l 选项后面跟的是库名(不包括前缀 lib 和后缀 .a),-I 告诉编译器在哪个目录下搜索头文件
# 打包动态库
# 源文件生成目标文件
[cen@VM-4-9-opencloudos lesson02-动静态库]$ g++ -fPIC -c add.cpp
[cen@VM-4-9-opencloudos lesson02-动静态库]$ ll
total 44
-rw-r--r-- 1 cen cen 136 Mar 20 20:25 add.cpp
-rw-r--r-- 1 cen cen 89 Mar 20 20:14 add.h
-rw-r--r-- 1 cen cen 2824 Mar 20 22:20 add.o
-rw-r--r-- 1 cen cen 2882 Mar 20 20:55 libcal.a
-rw-r--r-- 1 cen cen 247 Mar 20 21:44 Makefile
drwxr-xr-x 4 cen cen 32 Mar 20 20:58 mathlib
-rwxr-xr-x 1 cen cen 17056 Mar 20 20:27 test
-rw-r--r-- 1 cen cen 199 Mar 20 20:27 test.cpp
2
3
4
5
6
7
8
9
10
11
# shared 命令将目标文件生成动态库
[cen@VM-4-9-opencloudos lesson02-动静态库]$ g++ -shared -o libcal.so add.o
[cen@VM-4-9-opencloudos lesson02-动静态库]$ ll
total 60
-rw-r--r-- 1 cen cen 136 Mar 20 20:25 add.cpp
-rw-r--r-- 1 cen cen 89 Mar 20 20:14 add.h
-rw-r--r-- 1 cen cen 2824 Mar 20 22:20 add.o
-rw-r--r-- 1 cen cen 2882 Mar 20 20:55 libcal.a
-rwxr-xr-x 1 cen cen 16320 Mar 20 22:21 libcal.so
-rw-r--r-- 1 cen cen 247 Mar 20 21:44 Makefile
drwxr-xr-x 4 cen cen 32 Mar 20 20:58 mathlib
-rwxr-xr-x 1 cen cen 17056 Mar 20 20:27 test
-rw-r--r-- 1 cen cen 199 Mar 20 20:27 test.cpp
2
3
4
5
6
7
8
9
10
11
12
# 将头文件和动态库组织
mkdir -p mathlib/include
mkdir -p mathlib/lib
cp ./*.h mathlib/include
cp ./*.so mathlib/lib
2
3
4
# 使用动态库
[root@VM-4-9-opencloudos ~]# cp -r /home/cen/Linux/lesson02-动静态库/mathlib ./main
[root@VM-4-9-opencloudos ~]# ll
total 0
drwxr-xr-x 3 root root 37 Mar 21 10:31 main
[root@VM-4-9-opencloudos ~]# cd main
[root@VM-4-9-opencloudos main]# ll
total 4
-rw-r--r-- 1 root root 100 Mar 20 21:59 main.cpp
drwxr-xr-x 4 root root 32 Mar 21 10:31 mathlib
[root@VM-4-9-opencloudos main]# tree mathlib
mathlib
├── include
│ └── add.h
└── lib
└── libcal.so
2 directories, 2 files
[root@VM-4-9-opencloudos main]# g++ main.cpp -I./mathlib/include -L./mathlib/lib -lcal
[root@VM-4-9-opencloudos main]# ll
total 24
-rwxr-xr-x 1 root root 16784 Mar 21 10:32 a.out
-rw-r--r-- 1 root root 100 Mar 20 21:59 main.cpp
drwxr-xr-x 4 root root 32 Mar 21 10:31 mathlib
[root@VM-4-9-opencloudos main]# ./a.out
./a.out: error while loading shared libraries: libcal.so: cannot open shared object file: No such file or directory
[root@VM-4-9-opencloudos main]# ldd a.out
linux-vdso.so.1 (0x00007ffe3f1d9000)
libcal.so => not found
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007ff43f400000)
libm.so.6 => /lib64/libm.so.6 (0x00007ff43f69d000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007ff43f67d000)
libc.so.6 => /lib64/libc.so.6 (0x00007ff43f20c000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff43f789000)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
发现操作系统找不到动态库,因此可以将动态库拷贝到系统共享库路径下。
[root@VM-4-9-opencloudos main]# cp ./mathlib/lib/libcal.so /usr/lib64/
[root@VM-4-9-opencloudos main]# ldd a.out
linux-vdso.so.1 (0x00007ffe3f1d9000)
libcal.so => /usr/lib64/libcal.so (0x00007f0f0b0b6000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f0f0aef5000)
libm.so.6 => /lib64/libm.so.6 (0x00007f0f0adb4000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f0f0ab9d000)
libc.so.6 => /lib64/libc.so.6 (0x00007f0f09f9b000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0f0b2c8000)
[root@VM-4-9-opencloudos main]# ./a.out
100 + 300 = 400
2
3
4
5
6
7
8
9
10
11
# 场景分析
静态库适用于以下场景:
- 程序只使用库中的部分函数,且这些函数的大小比较小。
- 库的版本很少更改,且不需要与其他库进行交互。
- 程序需要在没有库文件的情况下运行,或者需要将程序和库打包成单个可执行文件。
动态库:适用于以下场景:
- 库的大小较大,或者它包含在多个程序中。
- 库的版本需要经常更改,或者需要与其他库进行交互。
- 需要在运行时加载库(例如插件)。
- 动态库在运行时并不会全部加载到内存中,而是以需要的方式进行加载。这种延迟加载可以帮助减少内存占用,并提高应用程序的性能。