Linux动态库和静态库
- Linux动态库和静态库
- 动静态库的基本原理
- 可执行程序的生成过程
- 动静态库的本质
- 认识动静态库
- 背后的库支持
- 动静态库的命名
- 静态链接示例
- 动静态库各自的特征
- 静态库
- 动态库
- 静态库的打包与使用
- 示例文件
- 打包
- 1. 生成目标文件
- 2. 打包静态库
- 3. 组织文件
- 使用 Makefile
- 使用
- 方法一:使用选项
- 方法二:拷贝到系统路径
- 动态库的打包与使用
- 打包
- 1. 生成目标文件
- 2. 打包动态库
- 3. 组织文件
- 使用 Makefile
- 使用
- 编译
- 运行时问题
- 解决方法
- 总结
Linux动态库和静态库
在Linux开发中,动态库和静态库是代码复用和程序构建的重要工具。无论是编写小型工具还是大型项目,理解动静态库的原理和使用方法都能极大提高开发效率。本文将从基本原理出发,逐步带您认识动静态库,分析它们的特征,并通过具体示例演示静态库和动态库的打包与使用过程。让我们开始吧!
动静态库的基本原理
可执行程序的生成过程
要理解动静态库的本质,首先需要了解源代码如何变成可执行程序。在Linux下,这一过程分为四个步骤:
- 预处理:处理头文件展开、去注释、宏替换和条件编译,生成
.i文件。例如,#include会被替换为头文件内容。 - 编译:进行词法分析、语法分析、语义分析和符号汇总,将代码翻译成汇编指令,生成
.s文件。 - 汇编:将汇编指令转换为二进制机器码,生成目标文件
.o。 - 链接:将多个
.o文件链接起来,生成最终的可执行程序。
假设有五个文件:test1.c、test2.c、test3.c、test4.c 和 main1.c,要生成可执行程序,我们需要:
- 分别编译生成
test1.o、test2.o、test3.o、test4.o和main1.o。 - 将这些目标文件链接,生成最终程序。
如果另一个项目需要用 test1.c 到 test4.c 与 main2.c 生成新程序,过程相同。但如果这些文件频繁复用,每次都重新编译会很麻烦。这时,我们可以将 test1.o 到 test4.o 打包成一个“库”,供不同项目直接链接使用。
动静态库的本质
动静态库本质上是目标文件(.o)的集合,是可执行程序的“半成品”。它们不包含主函数(main),只提供函数或方法的实现,供其他程序调用。库的出现解决了代码复用问题,避免了重复编译的繁琐。
认识动静态库
让我们通过一个简单程序认识动静态库:
#include <stdio.h>int main()
{printf("hello world\n"); // 调用库函数return 0;
}
编译并运行:
gcc -o mytest mytest.c
./mytest
输出:hello world
背后的库支持
这个程序能输出 hello world,是因为 gcc 在链接时自动引入了 C 标准库。可以用 ldd 查看依赖:
ldd mytest
输出示例:
linux-vdso.so.1 => (0x00007fff5f5ff000)
libc.so.6 => /lib64/libc.so.6 (0x00007f9c8e5b0000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9c8e9b4000)
其中,libc.so.6 是 C 标准动态库的软链接。查看其真实文件:
ls -l /lib64/libc.so.6
输出:libc.so.6 -> libc-2.17.so
进一步检查文件类型:
file /lib64/libc-2.17.so
输出:ELF 64-bit LSB shared object, x86-64, ...
这表明 libc-2.17.so 是一个动态共享库(.so 后缀)。
动静态库的命名
- Linux:
- 动态库:
.so(shared object),如libc.so.6。 - 静态库:
.a(archive),如libm.a。
- 动态库:
- Windows:
- 动态库:
.dll(dynamic link library)。 - 静态库:
.lib。
- 动态库:
库名规则:去掉前缀 lib 和后缀(.so 或 .a)及版本号,剩下的是库名。例如,libc.so.6 的库名是 c。
静态链接示例
默认情况下,gcc 使用动态链接。若要静态链接,添加 -static:
gcc -o mytest-s mytest.c -static
检查依赖:
ldd mytest-s
输出:not a dynamic executable
静态链接的可执行文件不依赖动态库,体积明显更大:
ls -lh mytest mytest-s
可能输出:
mytest:几十 KB(动态链接)。mytest-s:几 MB(静态链接)。
动静态库各自的特征
静态库
- 链接方式:编译时将库代码复制到可执行文件中。
- 运行时:不依赖外部库。
- 优点:独立性强,可单独运行。
- 缺点:文件体积大;多个程序加载相同静态库时,内存中会有重复代码,浪费资源。
动态库
- 链接方式:运行时才加载库代码,可执行文件中只包含函数入口地址表。
- 运行时:由操作系统从磁盘加载到内存,多个程序共享同一份代码。
- 优点:节省磁盘和内存空间,多个程序共享库时效率高。
- 缺点:依赖动态库,若库缺失则无法运行。
静态库的打包与使用
示例文件
我们用以下文件演示:
add.h:#pragma once extern int my_add(int x, int y);add.c:#include "add.h" int my_add(int x, int y) { return x + y; }sub.h:#pragma once extern int my_sub(int x, int y);sub.c:#include "sub.h" int my_sub(int x, int y) { return x - y; }
打包
1. 生成目标文件
gcc -c add.c -o add.o
gcc -c sub.c -o sub.o
-c:只编译,不链接,生成.o文件。
2. 打包静态库
ar -rc libcal.a add.o sub.o
ar:归档工具。-r:替换已有文件。-c:创建新库。libcal.a:生成静态库。
验证:
ar -tv libcal.a
输出示例:
rw-r--r-- 0/0 1234 Mar 19 12:34 2025 add.o
rw-r--r-- 0/0 1234 Mar 19 12:34 2025 sub.o
3. 组织文件
mkdir -p mathlib/include mathlib/lib
mv add.h sub.h mathlib/include/
mv libcal.a mathlib/lib/
目录结构:
mathlib/
├── include/
│ ├── add.h
│ └── sub.h
└── lib/└── libcal.a
使用 Makefile
CC = gcc
AR = ar
CFLAGS = -c
TARGET = libcal.a
OBJS = add.o sub.oall: $(TARGET)
add.o: add.c add.h$(CC) $(CFLAGS) add.c -o add.o
sub.o: sub.c sub.h$(CC) $(CFLAGS) sub.c -o sub.o
$(TARGET): $(OBJS)$(AR) -rc $(TARGET) $(OBJS)
output:mkdir -p mathlib/include mathlib/libcp *.h mathlib/include/cp $(TARGET) mathlib/lib/
clean:rm -f *.o $(TARGET) mathlib -r
.PHONY: all output clean
make:生成库。make output:组织文件。
使用
测试程序 main.c:
#include <stdio.h>
#include <add.h>int main()
{int x = 20, y = 10;int z = my_add(x, y);printf("%d + %d = %d\n", x, y, z);return 0;
}
方法一:使用选项
gcc main.c -I./mathlib/include -L./mathlib/lib -lcal -o main
-I:头文件路径。-L:库文件路径。-l:指定库名(cal)。
运行:
./main
输出:20 + 10 = 30
方法二:拷贝到系统路径
sudo cp mathlib/include/* /usr/include/
sudo cp mathlib/lib/libcal.a /lib64/
gcc main.c -lcal -o main
- 注意:仍需
-lcal指定库名。
动态库的打包与使用
打包
1. 生成目标文件
gcc -c -fPIC add.c -o add.o
gcc -c -fPIC sub.c -o sub.o
-fPIC:生成位置无关码,动态库必需。
2. 打包动态库
gcc -shared -o libcal.so add.o sub.o
-shared:生成共享库。
3. 组织文件
mkdir -p mlib/include mlib/lib
mv add.h sub.h mlib/include/
mv libcal.so mlib/lib/
使用 Makefile
CC = gcc
CFLAGS = -c -fPIC
TARGET = libcal.so
OBJS = add.o sub.oall: $(TARGET)
add.o: add.c add.h$(CC) $(CFLAGS) add.c -o add.o
sub.o: sub.c sub.h$(CC) $(CFLAGS) sub.c -o sub.o
$(TARGET): $(OBJS)$(CC) -shared -o $(TARGET) $(OBJS)
output:mkdir -p mlib/include mlib/libcp *.h mlib/include/cp $(TARGET) mlib/lib/
clean:rm -f *.o $(TARGET) mlib -r
.PHONY: all output clean
使用
编译
gcc main.c -I./mlib/include -L./mlib/lib -lcal -o main
运行时问题
运行 ./main 可能报错:
./main: error while loading shared libraries: libcal.so: cannot open shared object file
检查依赖:
ldd main
输出:libcal.so => not found
解决方法
-
拷贝到系统路径:
sudo cp mlib/lib/libcal.so /lib64/ ./main -
设置 LD_LIBRARY_PATH:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/user/mlib/lib ./main -
配置 /etc/ld.so.conf.d/:
echo "/home/user/mlib/lib" > mylib.conf sudo mv mylib.conf /etc/ld.so.conf.d/ sudo ldconfig ./mainldconfig:更新动态库缓存。
总结
- 静态库:打包用
ar,生成.a文件,编译时嵌入代码,独立性强但体积大。 - 动态库:用
gcc -shared,生成.so文件,运行时加载,节省空间但依赖库文件。 - 使用差异:静态库无需运行时配置,动态库需确保库路径可访问。
通过本文,您应该已经掌握了Linux下动静态库的原理和实践。希望这些知识能在您的开发中派上用场!
