1 引言
软件设计中有一条原则是模块化,当一个项目达到一定规模时,往往将模块编译成库的形式,这也有利于分工协作。在linux中库分为静态库和动态库。静态库,是在可执行程序链接时就已经将该库的代码链入到可执行程序中,在物理上成为执行程序的一部分,使用静态库编译的程序运行时无需该库文件支持,哪里都可以用,但是生成的可执行文件较大。动态库,是在可执行程序启动时动态加载到执行程序中,可以被多个可执行程序共享使用,当然共享的只是代码,共享库中的数据还是每个可执行程序都有一份。使用动态库编译生成的程序相对较小,但运行时需要库文件支持,如果机器里没有这些库文件就不能运行。
2 库命名规则
静态库一般命名为libxxx.a,xxx表示模块名,后缀.a是归档(archive)的意思。动态库的命名就比较讲究,一般以so为后缀,不过通常的命名风格为libxxx.so.major.minor.patch,这也只是一般约定,你可以随意命名。其中xxx表示模块名,major表示主版本号,minor表示次版本号,patch表示补丁号,如libbz2.so.1.0.4。
3 共享库的soname
soname称为简单共享名(short for shared object name),是共享库特有的,它的关键功能是提供了兼容性的标准,它一般的命名为libxxx.so.major,这样的话,只要主版本号不变,相关联的可执行程序是不必更新的(有过经验就会知道),所以比较方便。使用 readelf -d libxxx.so 命令可查看该共享库的soname(如果有的话)。
4 库的创建
看以下一段代码:
/* saysomething.c */
#include <stdio.h>
void say_something(const char *str)
{
printf("%s\n", str);
}
我将以这段代码为例来详细说明静态库与动态库的使用,如果能动手亲自操作一把,我相信你能无障碍使用静态库与共享库。
静态库的创建
静态库是若干个目标文件的简单打包,管理静态库的工具为ar。静态库创建很简单,先编译源代码,再将目标文件打包即可,命令如下:
gcc –c saysomething.c –o saysomething.o
ar –cr libsay.a saysomething.o
共享库的创建
共享库创建相对较麻烦,编译时须加一些特定选项,如下所示:
gcc –c –fpic saysomething.c –o saysomething.o
gcc –shared –Wl,-soname,libsay.so.1 –o libsay.so.1.0.0 saysomething.o
参数说明:
-fpic:生成位置无关代码
-shared:告诉编译器要生成共享库
-Wl:表明后面用逗号隔开的参数是传给链接器的
-soname:指明soname
编译后将生成库文件libsay.so.1.0.0,其soname为libsay.so.1。
5 链接与运行
库文件只用于链接和运行阶段,对于静态库,只在链接时需要,而对于共享库,两个阶段都需要。链接和运行关键是要让编译器能找到该库,也就是库文件搜索路径问题。对于链接,可以用-L指定库文件搜索路径,而对于运行,则可用-R或-rpath指定。库文件的引用可以写出库文件的全名,也可用 –lxxx 的方式引用,编译器会按照命名规则自动扩展出全名。
库文件有两个默认搜索路径——/lib和/usr/lib,如果编译时没有用-L或-R指定路径或是在指定路径找不到相应的库文件,则会在这两个目录下查找。
除了这些方法,还可通过设置环境变量 LD_LIBRARY_PATH 的方式让编译器找到库文件,对于动态库,还可通过修改 /etc/ld.so.conf 文件来配置搜索路径。
如果存在同名的静态库和共享库,则共享库优先使用。
搜索方式按优先级从高到低排列如下(用-l方式指定的库):
1)-L或-R指定的路径
2)环境变量LD_LIBRARY_PATH指定的动态库搜索路径,经测试发现其只对运行时有效
3)配置文件/etc/ld.so.conf中指定的动态库搜索路径
4)默认的动态库搜索路径/lib /usr/lib