「Linux 系统编程」静态库与动态库
1 静态库与动态库简介
1.1 静态库介绍
静态库(Static Library)是编译时被 完整复制到目标程序中 的库文件,程序运行时不再依赖原库文件。
在 Linux 系统中,静态库文件以 .a
为扩展名,例如 libmath.a
。
当静态库文件被改动,需要重新编译链接程序才能生效。
1.2 动态库介绍
动态库(Dynamic Library,又称共享库 Shared Library)在程序编译时仅记录引用信息,程序运行时才被加载到内存并共享使用。
在 Linux 系统中,动态库文件以 .so
为扩展名,例如 libmath.so
。
当动态库更新后,无需重新编译程序(只要接口兼容),但运行时需保证库存在且路径正确。
1.3 静态库与动态库对比
静态库在编译时 “嵌入” 程序,运行独立但体积大;动态库在运行时 “共享”,体积小但依赖外部文件。
通常来说,静态加载的速度会快一些,而动态库速度慢一些,但是节省内存。
1.4 gcc 编译默认链接的动态库
在用 gcc 编译 .c
程序后,可以通过 ldd
命令查看可执行文件依赖的动态库:
1 |
|
可以看到,libc.so.6
和 ld-linux-x86-64.so.2
就是默认链接的核心动态库。
libc.so.6
提供 C 语言标准函数(如 printf、scanf、malloc、free、字符串处理函数等),是几乎所有 C 程序的 基础依赖。
ld-linux-x86-64.so.2
是 动态链接器相关库,负责在程序运行时加载所需的动态库(包括上述 libc.so 等),并解析库中的函数引用。
2 Linux 静态库制作
2.1 制作步骤
-
创建源代码文件:
写了一些函数,分别保存在
add.c
、sub.c
和hello.c
文件中。1
2
3
4// add.c
int add(int a, int b){
return a + b;
}1
2
3
4// sub.c
int sub(int a, int b){
return a - b;
}1
2
3
4
5
6// hello.c
#include <stdio.h>
void hello(){
printf("Hello, Marisa!\n");
}
-
创建头文件
1
2
3
4
5
6
7
8
9// marisalib.h
#ifndef _MARISA_H_
#define _MARISA_H_
int add(int, int);
int sub(int, int);
void hello();
#endif此处增加了 头文件保护符,避免重复包含头文件内容,防止出现重定义错误、潜在的逻辑错误等。
-
将源代码编译为目标文件
1
2
3gcc -c add.c -o add.o
gcc -c sub.c -o sub.o
gcc -c hello.c -o hello.o
-
打包成静态库
1
ar rcs libmarisa.a add.o sub.o hello.o
ar
: 归档工具rcs
: r(替换旧成员)、c(创建库)、s(添加索引)
静态库名格式必须为
lib[库名称].a
。
在完成上述工作后,可以通过 ar -t libmarisa.a
命令查看库中的内容:
2.2 使用示例
写一个测试程序 test.c
:
1 |
|
通过以下命令进行编译链接:
1 |
|
-L./
:-L
指定查找库的路径。在当前目录查找库-lmarisa
:-l
指定链接库的名称。链接libmarisa.a
,名称需要省略lib
和.a
。
最后运行程序:
1 |
|
2.3 注意事项
如果头文件、库所在的路径不在测试程序所在目录,例如 .h
头文件位于 ./include
中,.a
库文件位于 ./libs
中。此时,需要用 -I
指定查找头文件的路径:
1 |
|
假如存在静态库与动态库同名,此时之前的编译命令会优先链接动态库。如果要强制使用静态库,需要改成如下命令形式:
1 |
|
3 Linux 动态库制作
3.1 制作步骤
-
创建源代码文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// marisaops.c
#include <stdio.h>
#include "marisalib.h"
int add(int a, int b){
return a + b;
}
long long mul(long long a, long long b){
return a * b;
}
void hello(){
printf("Hello, Kirisame Marisa!\n");
}
-
创建头文件
1
2
3
4
5
6
7
8
9// marisalib.c
#ifndef _MARISA_H_
#define _MARISA_H_
int add(int, int);
long long mul(long long, long long);
void hello();
#endif
-
编译成位置无关代码
1
gcc -c -fPIC marisaops.c -o marisaops.o
-fPIC
:生成位置无关代码(Position Independent Code),是动态库必需的特性。
-
生成动态库
1
gcc -shared -o libmarisa.so marisaops.o
-shared
:指定生成共享库- 命名规范:
lib[库名称].so
(如libmarisa.so
)
上述工作完成后,可以查看动态库信息:
1 |
|
3.2 使用示例
写一个测试程序 test.c
:
1 |
|
编译链接动态库:
1 |
|
-L./
:指定库搜索路径(当前目录)-lmarisa
:链接libmarisa.so
(省略lib
前缀和.so
后缀)
此时直接运行 ./test
会出现找不到动态库位置的错误:
在程序 运行阶段,需要为动态链接器 提供动态库所在目录位置:
-
临时设置(仅当前终端有效)
在编译之后,执行如下命令:
1
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
然后再运行程序
./test
即可。export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
指定动态链接器在运行时搜索共享库(.so 文件)的路径为当前路径。其中
.
表示当前工作目录(点号.代表当前目录),确保程序首先在当前目录中查找需要的动态库。$LD_LIBRARY_PATH
引用该变量当前已有的值。这样可以在添加新路径的同时保留原有的搜索路径。
-
永久设置(添加到用户配置文件)
任何编辑器都行,打开
~/.bashrc
:1
gedit ~/.bashrc
将这
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
添加到文件中,并执行:1
source ~/.bashrc # 使配置文件立即生效
需要注意的是,如果动态库的路径被修改了,在重新编译过后,~/.bashrc
文件中的运行时指定动态库路径命令也需要修改。
3.3 系统配置链接方法
可以通过配置系统文件 /etc/ld.so.conf
来实现动态库链接,相比临时设置 LD_LIBRARY_PATH
,具有永久性、系统级和更安全的优点。
用任意编辑器打开 /etc/ld.so.conf
:
1 |
|
将 动态库绝对路径 写入保存:
然后执行 sudo ldconfig -v
(更新系统库缓存)使得配置文件生效,其中 -v
增加生效可视化过程。之后再执行 ./test
程序: