前言:
在讲解Linux下库文件的知识之前,请先来简单了解一下C语言程序的编译过程。
在Linux系统gcc编译器下,C语言程序的编译过程基本可分为预处理、编译、汇编、链接四个过程。
其中,最后一步的链接,就是将系统的库文件(或是程序员自己编写的库文件)与经过汇编步骤生成的所有.o
文件(可重定位文件1)合并生成最后的example
可执行文件。库文件的使用也主要与此部分有关。
Linux 下库文件简介
1. 库文件是什么?
由以上C语言编译过程可以知道,编译最后要生成可执行文件example
,必须去链接一些所谓的“库文件”,那这些神秘的库文件又是什么呢?
其实,库文件就是包含一系列子程序的集合体,也可以简单理解为一堆.o
文件的打包集合。在库文件中封装了一系列函数的实现,而调用者通过加载库文件就可以实现对库中的函数的调用。(注:根据实现和使用方式的不同又可分为静态库和动态库,后面会详细介绍;编译过程默认链接系统标准C库和gcc内置库,外部库需手动链接)
一方面,对库文件的灵活使用可以使得编程更加便捷、高效。另一方面,对库的使用也更好的保证了部分程序源码的私密性(如某个函数中包含了重要的算法,而你又不想公开这个算法,那么便可以将其编入库中)。同时,使用库文件还可以减少重复编译的时间,增强程序的模块化。
2. 静态库与动态库的区别
静态库与动态库二者之间最大的不同点在于代码被载入的时刻不同。静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。而动态库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。
静态库 | 动态库 |
---|---|
在程序编译阶段载入程序 | 在程序运行阶段动态加载 |
使得可运行程序体积较大 | 可运行程序体积基本不变 |
程序的运行不依赖于库,一次编译,永久有效,运行效率较高 | 程序的运行依赖于库,需动态加载,运行效率较低 |
浪费大量储存空间 | 节省大量储存空间 |
程序编译阶段载入程序,无法进行后期的维护和升级 | 程序运行阶段动态加载,与程序耦合性较低,方便进行后期的维护和升级 |
静态库与动态库各有利弊,但由于动态库更加方便进行后期的维护和升级,动态库在实际项目中更加常用。
Linux 下库文件制作
假设有两个文件 a.c
,b.c
,下面我们分别将它们制作成静态库libxxx.a
和动态库libxxx.so
(xxx为库的随意名字,静态库和动态库名称不一定要相同)。
1. 制作.o
文件:
gcc a.c -o a.o -c -fPIC
gcc b.c -o b.o -c -fPIC
由此得到文件 a.o
,b.o
2. 制作静态库:
ar rcs libxxx.a a.o b.o
由此得到静态库libxxx.a
3. 制作动态库:
gcc a.o b.o -o libxxx.so -shared -fPIC
由此得到动态库libxxx.so
Linux 下库文件的使用
1. 编译时库的使用
gcc example.c -o example -L dir/ -lxxx
备注:
dir/
中存放库中函数声明的头文件- 除去前缀后缀之外的,如
libxxx.a
,库名就是xxx
- 当同一个目录下,有重名的动态、静态库,默认情况下会自动链接动态库,如果要指定静态库:
gcc example.c -o exmaple -L dir/ -lxxx -static
2. 运行时库的使用
静态库由于在程序编译阶段就已经编入了程序中,因此运行时就不再需要,而动态库则需要动态链接。
动态库链接的3种方法:
- 设置环境变量 LD_LIBRARY_PATH:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:dir/
注:dir/
就是动态库的所在路径,也可以将命令写入系统配置文件使其永久生效。
- 将库文件,放入系统的标准库路径中:
/lib
/usr/lib
/usr/local/lib
...
注:避免使用这种方法,污染了系统!
- 在编译的阶段,告诉程序以后动态库会放在哪里:
告诉程序 example,运行时动态库的路径就在 dir/ 里面
gcc example.c -o example -L . -lxxx -Wl,-rpath=dir/
此篇文章仅供大家参考学习,内容较为浅显,如有不足之处,还请见谅!
所谓的可重定位,指的是该文件虽然已经包含可以让处理器直接运行的指令流,但是程序中所有的全局符号(函数和全局变量)尚未定位,地址仍为0,程序无法正确运行。该文件中所调用的函数或是全局变量的声明可能不在此文件中,需要结合其他文件中的信息才能重新定位此函数或是变量地址,程序才能正确运行。 ↩︎