1静态库和共享库
*本节就如何创建和使用程序库进行论述。所谓“程序库”,简单说,就是包含了数据
和执行码的文件。其不能单独执行,可以作为其它执行程序的一部分来完成某些功能。库的
存在,可以使得程序模块化,可以加快程序的再编译,可以实现代码重用,可以使得程序便
于升级。程序库可分静态库(static library)和共享库(shared object)。
A:静态库
是在可执行程序运行前就已经加入到执行码中,成为执行程序的一部分;共享库,是在
执行程序启动时加载到执行程序中,可以被多个执行程序共享使用。
建议库开发人员创建共享库,比较明显的优势在于库是独立的,便于维护和更新;而静
态库的更新比较麻烦,一般不做推荐。然而,它们又各有优点,后面会讲到。
节所讲述的执行程序和库都采用ELF(Executable and Linking Format)格式,尽管GNU
GCC工具可以处理其它格式,但不在本节的讨论范围。
静态库可以认为是一些目标代码的集合。按照习惯,一般以“.a”做为文件后缀名。使
用ar(archiver)命令可以创建静态库。因为共享库有着更大的优势,静态库已经不经常使
用。但静态库使用简单,仍有使用的余地,并会一直存在。有些Unix系统,如Solaris 10,
已经基本废弃了静态库。
静态库在应用程序生成时,可以不必再编译,节省再编译时间。但在编译器越来越快的
今天,这一点似乎已不重要。如果其他开发人员要使用你的程序,而你又不想给其源码,提
供静态库是一种选择。从理论上讲,应用程序使用了静态库,要比使用动态加载库速度快
1-5%,但实际上可能并非如此。由此看来,除了使用方便外,静态库可能并非一种好的选
择。
要创建一个静态库,或要将目标代码加入到已经存在的静态库中,可以使用以下命令:
ar rcs libmylib.a file1.o
file2.o以上表示要把目标码file1.o和file2.o加入到静态库libmylib.a中(ar的参数
r)。若libmylib.a不存在,会自动创建(ar的参数c)。然后更新.a文件的索引,使之包含新
加入的.o文件的内容(ar的参数s)。
静态库创建成功后,需要链接到应用程序中使用。使用gcc的-l选项来指定静态库,使
用-L参数来指定库文件的搜索路径。比如上述例子应指定-lmylib,所有库文件名都以lib开
头,开头的lib在指定参数时应省略。-l和-L之后都直接带参数而不跟空格。
在使用gcc时,要注意其参数的顺序。-l是链接器选项,一定要放在被编译的文件名称
之后;若放在文件名称之前则会连接失败,并会出现莫名其妙的错误。这一点切记。
B:共享库
共享库的创建比较简单,基本有两步。首先使用-fPIC或-fpic创建目标文件,PIC或
pic表示位置无关代码,然后就可以使用以下格式创建共享库了: gcc -share -Wl,-
soname,your_soname -o library_namefile_list library_list 下面是使用a.c和b.c创建
库的示例:
相关命令:
gcc –fPIC –c XX.c
gcc –fpic –c XX.c
gcc -shared -Wl,-soname,libmyab.so.1-o libmyab.so.1.0.1 a.o b.o
libmyab.so.1称之为soname,
libmyab.so.1.0.1称之为realname
libmyab.so 称之为linker name
按照共享库的命名惯例,每个共享库有三个文件名:real name、soname和linker
name。真正的库文件(而不是符号链接)的名字是realname,包含完整的共享库版本号。
soname是一个符号链接的名字,只包含共享库的主版本号,主版本号一致即可保证库函
数的接口一致,因此应用程序的.dynamic段只记录共享库的soname,只要soname一致,这个
共享库就可以用。如libmyab.so.1和libmyab.so.2是两个主版本号不同的libmyab,有些应
用程序依赖于libmyab.so.1,有些应用程序依赖于libmyab.so.2,但对于依赖libmyab.so.1
的应用程序来说,真正的库文件不管是libmyab.so.1.10还是libmyab.so.1.11都可以用,所
以使用共享库可以很方便地升级库文件而不需要重新编译应用程序,这是静态库所没有的优点。
注意libc的版本编号有一点特殊,libc-2.8.90.so的主版本号是6而不是2或2.8。
linker name仅在编译链接时使用,gcc的-L选项应该指定linkername所在的目录。有
的linker name是库文件的一个符号链接,有的linker name是一段链接脚本。例如上面的
libc.so就是一个linkername,它是一段链接脚本:
C:共享库加载
在所有基于GNUglibc的系统中,在启动一个ELF二进制执行程序时,一个特殊的
程序“程序装载器”会被自动装载并运行。在linux中,这个程序装载器就是/lib/ldlinux.
so.X(X是版本号)。它会查找并装载应用程序所依赖的所有共享库。被搜索的目录保存在/etc/ld.so.conf文件中。当然,如果程序的每次启动,都要去搜索一番,势必效率不
堪忍受。Linux系统已经考虑这一点,对共享库采用了缓存管理。ldconfig就是实现这一功
能的工具,其缺省读取/etc/ld.so.conf文件,对所有共享库按照一定规范建立符号连接,
然后将信息写入/etc/ld.so.cache。/etc/ld.so.cache的存在大大加快了程序的启动速
度。
D:案例说明:
D1:创建一个目录,mycal
mkdir mycal
D2:创建4个c文件盒1个.h,分别实现加减乘除
add.c
sub.c
mul.c
dive.c
common.h
编写main.c
#include <stdio.h> #include "common.h"
int main(void) { printf("%d", add(5, 4)); return 0; } |
D3制作静态库(加上-fPIC后生成的目标文件和位置无关,注意要加上这个)
查看静态库信息:
使用静态库(程序扔到任何电脑都可以运行,因为静态库相当于包含到了静态库中):
gcc -Isrc main.c libmycal.a –o app(加-Isrc是为类引用common.h)
ldd app命令检测的是共享库,不检测静态库
上面的过程相当于libmycal.a和main.c进行组合,生成app
D4制作共享库,
移动目录如下结构:
这时候生成了realname: libmycal.so.1.10
D5设置共享库加载路径
打开共享库路径配置文件
sudo vi /etc/ld.so.conf
最后一行添加mycal路径,截图如下,若不配置下面的信息,它后后面的gcc执行的命令和ldd命令回报没有找到库的提示信息。(下面配置告诉库所在的位置)
sudo ldconfig –v(输入后输出一堆结果,如果还是发现没有生成libmycal.so.1,这时候要去除缓存文件/etc/ld.so.cache 命令是:sudo rm –rf/etc/ld.so.cache
gcc main.c libmycal.so.1.10 -o app
ldd app查看程序运行的时候的依赖信息
如果用ldd app查看还是没有看到依赖,要去掉依赖文件,要删除的缓存文件是:/etc/ld.so.cache
注意:共享库和main.c共同使用的时候,在这个过程中相当于生成了main.o,符号记录表。app运行的时候依赖共享库版本。