一、什么是静态库、动态库???
静态库:这类库的名字一般是libxxx.a,xxx为库的名字。利用静态函数库编译成的文件比较大,因为整个函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译。
动态库:这类库的名字一般是libxxx.M.N.so,同样的xxx为库的名字,M是库的主版本号,N是库的副版本号。当然也可以不要版本号,但名字必须有。相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。
动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。linux系统有几个重要的目录存放相应的函数库,如/lib /usr/lib。当要使用静态的程序库时,连接器会找出程序所需的函数,然后将它们拷贝到执行文件,由于这种拷贝是完整的,所以一旦连接成功,静态程序库也就不再需要了。然而,对动态库而言,就不是这样。动态库会在执行程序内留下一个标记指明当程序执行时,首先必须载入这个库。由于动态库节省空间,linux下进行连接的缺省操作是首先连接动态库,也就是说,如果同时存在静态和动态库,不特别指定的话,将与动态库相连接。
二、如何创建及使用静态库、动态库
2.1、静态链接库
制作静态链接库的过程中要用到gcc和ar命令。
ar命令:用来维护链接编辑库所使用的库索引。ar命令将一个或多个指定的文件并入单个写成ar压缩文档格式的压缩文档文件。当ar命令创建库的时候,它创建可传输格式的报头;当他创建或者更新库的时候,它重建符号表。
常用选项:
-c:创建一个库,不管这个库存不存在,都将创建。
-r:在库中插入模块(替换)。若插入的模块在库中存在,则会进行替换。
-s:创建目标文件索引,在创建较大的库时能加快时间。不困是否修改了库内容,都将重新符号表。
-t:将库目录写到标准输出,如果指定文件名称,则显示指定的文件。如果不指定文件名称的话,则显示库目录的所有文件。
-v:将建立新库的详细的文件逐个写到标准输出
例:准备两个库的源码文件st1.c和st2.c,用它们来制作库libmytest.a,如下:
静态库文件libmytest.a已经生成,用file命令查看其属性,发现它确实是归档压缩文件。用ar -t libmytest.a可以查看一个静态库包含了那些obj文件:
接下来我们就写个测试程序来调用库libmytest.a中所提供的两个接口print1()和print2()。
在gcc -o test test.c -L. -lmytest 中,-lmytest表示要使用的库,“-L.”表示这个库再当前目录下,-L表示定制目录。
静态库链接时搜索路径顺序:
1. ld会去找GCC命令中的参数-L
2. 再找gcc的环境变量LIBRARY_PATH
3. 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的
2.2、动态库
栗子:我们有一个头文件test.h和2个源文件test1.c、test2.c将他们制作成一个名为libtest.so的动态链接库文件:
创建动态库:
-shared:该选项指定生成动态连接库。
-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
动态链接库的使用有两种方法:既可以在运行时动态链接,也可以在代码中通过函数进行动态加载。
1、动态链接:
使用“-lmytest”标记来告诉GCC驱动程序在连接阶段引用共享函数库libmytest.so。“-L.”标记告诉GCC函数库可能位于当前目录。否则GNU连接器会查找标准系统函数目录。
ldd可以查看一个可执行程序依赖的文件。这里我们注意,ldd的输出它说我们的libmytest.so它没找到。这是为什么呢???
这个问题就与动态库链接、执行时搜索路径有关:
1. 编译目标代码时指定的动态库搜索路径
2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径
3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径
4. 默认的动态库搜索路径/lib
5. 默认的动态库搜索路径/usr/lib
Linux系统中动态链接库的配置文件一般在/etc/ld.so.conf文件内,它里面存放的内容是可以被Linux共享的动态联库所在的目录的名字。
然后/etc/ld.so.conf.d/目录下存放了很多*.conf文件,如下:
其中每个conf文件代表了一种应用的库配置内容。
在/etc目录下还存在一个名叫ld.so.cache的文件。它是动态链接库的缓存文件。为了使得动态链接库可以被系统使用,当我们修该了/etc/ld.so.conf或/etc/ld.so.conf.d/目录下的任何文件,或者往那些目录下拷贝了新的动态链接库文件时,都需要运行一个很重要的命令:ldconfig,该命令位于/sbin目录下,主要的用途就是负责搜索/lib和/usr/lib,以及配置文件/etc/ld.so.conf里所列的目录下搜索可用的动态链接库文件,然后创建处动态加载程序/lib/ld-linux.so.2所需要的连接和(默认)缓存文件/etc/ld.so.cache(此文件里保存着已经排好序的动态链接库名字列表)。
也就是说:当用户在某个目录下面创建或拷贝了一个动态链接库,若想使其被系统共享,可以执行一下”ldconfig目录名”这个命令。此命令的功能在于让ldconfig将指定目录下的动态链接库被系统共享起来,即:在缓存文件/etc/ld.so.cache中追加进指定目录下的共享库。
因此要解决上面的问题的话,我们自己开发的共享库就可以将其拷贝到/lib、/etc/lib目录里,又或者修改/etc/ld.so.conf文件将我们自己的库路径添加到该文件中,再执行ldconfig命令。
在这里我将路径拷贝到/etc/ld.so.conf文件内。
2、动态加载
用一组函数来完成,需要包含头文件:dlfcn.h.
相关函数:
const char *dlerror(void):当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。
void *dlopen(const char *filename, int flag):
功能:用于打开指定名字(filename)的动态链接库,并返回操作句柄。调用失败时,将返回NULL值,否则返回的是操作句柄。
参数:
filename:如果名字不以“/”开头,则非绝对路径名,将按下列先后顺序查找该文件。
(1)用户环境变量中的LD_LIBRARY值;
(2)动态链接缓冲文件/etc/ld.so.cache
(3)目录/lib,/usr/lib
flag:表示在什么时候解决未定义的符号(调用)。取值有两个:
1) RTLD_LAZY : 表明在动态链接库的函数代码执行时解决。
2) RTLD_NOW :表明在dlopen返回前就解决所有未定义的符号,一旦未解决,dlopen将返回错误。
void *dlsym(void *handle, char *symbol):
功能: 根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的函数的执行代码地址。由此地址,可以带参数执行相应的函数。
int dlclose (void *handle):
功能:用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。2.2在程序中使用动态链接库函数。
使用动态链接库,源程序中要包含dlfcn.h头文件,写程序时注意dlopen等函数的正确调用,编译时要采用-rdynamic选项与-ldl选项(不然编译无法通过),以产生可调用动态链接库的执行代码。