现在的位置: 首页 > 编程语言 > 正文

静态链接库和动态链接库

2019年11月06日 编程语言 ⁄ 共 11819字 ⁄ 字号 暂无评论

库,是一种封装机制,简单说是把所有的源代码编译成目标代码后打成的包.库的开发者除了提供库的目标代码外,还提供一系列的头文件,头文件中就包含了库的接口,另外还有一些必要的注释.库函数根据是否被编译到程序内部而分为静态链接库(static)和动态链接库(dynamic).
简而言之,库是函数的集合,或.o文件的集合.链接器(LD)用于将用户自己的.o文件与库函数链接在一起.

一、静态链接库(静态函数库)
扩展名:这类函数通常扩展名类似于libxxx.a -->"lib"、"a"必须都加上,中间的"xxx"表示库的名称
编译行为:当程序在使用时,整个静态函数库的所有数据都会整合到执行文件中.即当用户进行编译操作时,静态函数库会加入到执行文件中,所以,利用静态函数库生成的可执行文件会比较大一些.
举例:一工程中包含一个file.c源文件,当使用静态函数库进行编译时,系统会将file.c源文件和静态函数库放在一起,然后再生成一个可执行文件file.elf
独立执行状态:静态函数库最大特点是,编译成功的可执行文件可以独立运行,不需要再向外部要求读取函数库的内容(因为函数库的内容已经随着源文件一起编译成了可执行文件).
升级难以程度:静态函数库最大缺点是升级难度大.比如当函数库升级后,还必须将源文件和升级后的函数库进行重新编译.这样才能得到升级后的可执行文件,因此操作比较麻烦.

二、动态链接库(动态函数库,又称共享库)
扩展名:这类函数通常扩展名类似于libxxx.so
编译行为:动态函数库在编译时,在程序中只有一个"指向(pointer)"的位置而已.或者说,最终生成的可执行程序中只是记录了动态链接库的名字、路径等其它少量的登记信息.因此,动态链接库并没有随着源文件一起整合到可执行文件中,而是当执行文件要用到函数库时,程序才会去读取.由于可执行文件中仅具有动态函数库所在的指针(或地址),并不包含函数库的内容,所以它会小一点.
举例:一工程中包含一个file.c源文件,当使用动态函数库进行编译时,系统只会将file.c源文件进行编译生成可执行文件file.o,并且在file.o中包含了动态链接库在系统的地址或者路径.当执行file.o时,系统会到指定的地址或路径去读取动态链接库.
独立执行状态:由于动态链接库是在需要的进行读取,所以函数库必须存在,而且函数库所在目录也不能改变.由于可执行文件中只有"指针",即当要采用该动态函数库时,程序会主动去某个路径下读取,因此动态函数库不能随意移动或删除,这会影响很多代码的执行.
升级难以程度:虽然这类可执行文件无法独立运行,然而由于具有指向功能,所以,当函数库升级后,执行文件不再需要进行重新编译,执行文件会直接指向新的函数库文件(前提是函数库新旧版本中文件名及路径不变).

三、对静态函数库和动态函数库的补充说明
通俗的讲,静态库是在链接阶段被链接的,所以生成的可执行文件就不受库的影响,即使库被删除了,程序依然可以成功运行.而动态库是在程序执行的时候被链接的,所以,即使程序编译完,库仍须保留在系统上,以供程序运行时调用.
链接静态库其实从某种意义上来说是一种粘贴复制,只不过它操作的对象是目标代码而不是源码而已.因为静态库被链接后就直接嵌入可执行文件中了,但这样就带来了两个问题.
(1)系统空间被浪费.
这是显而易见的.如果多个文件链接了同一个库,则每一个生成的可执行文件就都会有一个库的副本,必然会浪
费系统空间.
(2)即使是精心调试的库,也难免会有错.一旦发现了库中有bug,挽救起来就比较麻烦了.必须一一把链接该库的程序
找出来,然后重新编译.而动态库的出现正弥补了静态库的以上弊端.因为动态库是在程序运行时被链接的,所以磁盘上只须保留一份副本,因此节约了磁盘空间.如果发现了bug或要升级也很简单,只要用新的库把原来的替换掉就行了.
是不是静态库就一无是处了呢?非也.如果你用libpcap库编了一个程序,要给被人运行,而他的系统上没有装pcap库,最简单的办法就是把所有要链接的库都做成静态库,这样,就可以在他人的系统上直接运行该程序了.
但正因为动态库在程序运行时被链接,故程序的运行速度和链接静态库的版本相比必然会打折扣.动态库的不足相对于它带来的好处下简直是微不足道的,所以链接程序在链接时一般是优先链接动态库的,除非用-static参数指定链接静态库.

四、Linux中的静态/动态函数库
由于Linux系统里的套件依赖性太复杂,如果使用太多的静态函数库,势必会给系统升级带来很大的麻烦.因此在Linux中,多采用动态函数库,
最主要的原因就是函数库升级方便.
绝大多数的函数库都放在:
/lib
/usr/lib
/lib/modules (Linux核心的函数库)

五、查看某文件中使用库函数的类型
方法: file 文件名
命令file可以用来判断文件类型.在file命令下,所有文件都会原形毕露.有时在windows下用浏览器下载tar.gz或tar.bz2文件,后缀名会变成奇怪的tar.tar,到linux有些新手就不知怎么解压了.但linux下的文件类型并不受文件后缀名的影响,所以可以先用命令file xxx.tar.tar看一下文件类型,然后用tar加适当的参数解压.
在Linux中还可以使用ldd命令用来打印目标程序(由命令行参数指定)所链接的所有动态库的信息.如果目标程序没有链接动态库,则打印
“not a dynamic executable”.
格式: ldd [filename]
举例: [root@localhost /]#ldd /usr/bin/passwd
linux-gate.so.1 =>
(0x00d19000)
... ...
libpam_misc.so.0 => /lib/libpam_misc.so.0
(0x00bd6000)
可以看出,该/usr/bin/passwd使用的是动态库函数.

六、创建动态库函数及gcc的编译、链接
执行可执行文件时,用户必须明确动态函数库和头文件的路径.在Linux中,这种路径可分为系统路径和当前路径.因此,就会有4种情况产生,1是动态函数库和头文件均在当前路径下;2是动态函数库和头文件均在系统路径下.3是动态函数库在系统路径下,头文件在当前路径下;4是动态函数库在当前路径下,头文件在系统路径下.下面就这4中情况分别进行说明.

1、动态库函数和头文件都在当前路径下:
步骤1:创建动态库函数
创建一个头文件:test.h
创建三个.c文件:test1.c、test2.c、test3.c
将这4个文件编译成一个动态库:libtest.so
(1)使用vim编辑器分别创建这4个文件
test.h :
#include
#include
extern void test1(void);
extern void test2(void);
extern void test3(void);

test1.c
#include "test.h"
void test1(void)
{
printf("this is a test1 program!/n");
}

test2.c
#include "test.h"
void test2(void)
{
printf("this is a test2 program!/n");
}

test3.c
#include "test.h"
void test3(void)
{
printf("this is a test3 program!/n");
}
(2)使用gcc创建动态函数库
[root@loalhost lishuai]#gcc test1.c test2.c test3.c -shared -o libtest.so
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test2.c test3.c test.h libtest.so
这样就成功创建了动态函数库libtest.so
注释:
-shared : 创建动态函数库
libtest.so : 这是动态函数库名所规定的书写格式.以"lib"开头,以"so"结尾,中间是动态函数库名.
制作动态函数库时,不需要加上头文件,即:
不能写成:gcc test1.c test2.c test3.c test.h -shared -o libtest.so
制作动态链接库时,gcc不需要参数-L、-l、-I,这些参数只是在链接函数库时才使用,而不是在制作函数库时使用.

步骤2:创建需要链接动态函数库的主函数
在步骤1中已经成功生成了一个用户自己的动态链接库libtest.so,下面通过一个程序来调用这个库里的函数.程序的源文件为:test.c
test.c :
#include "test.h"
int main(void)
{
test1();
test2();
test3();
return 0;
}
步骤3:将主函数与动态函数库链接生成可执行文件
在步骤1中已经创建了动态函数库.
在步骤2中已经创建了需要该动态函数库的主函数.
因此,下面就需要生成可执行文件了.
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test2.c test3.c test.c test.h libtest.so
[root@loalhost lishuai]#gcc test.c -L./ -ltest -o test.elf
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test2.c test3.c test.c test.elf test.h libtest.so
注释:gcc的编译选项
-L 路径 :指定额外的库函数搜索路径DIR,因此-L./表示指定函数库的路径为当前路径
-l 函数库名 :指定链接时需要的其它函数库,这里特别注意的是l后跟函数库的名字,即test,而不是libtest.so

步骤4:执行该可执行文件test.elf
在执行该二进制文件之前,先使用ldd来查看在文件所使用的动态链接库的信息.
[root@loalhost lishuai]#ldd test.elf
libtest.so => not found
系统却提示没有找到该动态链接库libtest.so.
这是由于头文件和动态库都在当前路径下,系统无法找到,可以通过修改环境变量LD_LIBRARY_PATH的值,让系统找到动态函数库libtest.so.
[root@loalhost lishuai]#echo $LD_LIBRARY_PATH
此时系统什么都没有显示,表明环境变量LD_LIBRARY_PATH没有值.
[root@loalhost lishuai]#export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH
这样就将当前目录加入了搜索路径中.
执行该可执行文件test.elf:
[root@loalhost lishuai]#./test.elf
this is a test1!
this is a test2!
this is a test3!

至此,就实现了动态函数库的制作及加载.
2、动态库函数和头文件都在系统路径下:
步骤1:重复执行1、中的步骤1、2
步骤2:将动态函数库放在系统路径下/lib或/usr/lib
将头文件放在系统路径下/usr/include
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test2.c test3.c test.c test.h libtest.so
[root@loalhost lishuai]#mv libtest.so /lib
[root@loalhost lishuai]#mv test.h /usr/include
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test2.c test3.c test.c
步骤3:将主函数与动态函数库链接生成可执行文件
在步骤1中已经创建了动态函数库,且已经创建了需要该动态函数库的主函数.因此,下面就需要生成可执行文件了.
[root@loalhost lishuai]#gcc test.c -o test.elf -ltest -I/usr/include
或者:
[root@loalhost lishuai]#gcc test.c -o test.elf -ltest
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test2.c test3.c test.c test.elf
[root@loalhost lishuai]#./test.elf
this is a test1!
this is a test2!
this is a test3!
注释:由于已将动态函数库放在了系统路径下,所以,链接时不再需要定义参数"-L"了.系统会自动到相应路径下查找.但仍然需要指定该动态库函数的名字,即仍然必须定义参数"l"(有路径,也需要有该路径下函数库名).无论是动态函数库还是静态函数库,对于头文件的位置要求并不严格,这里对参数"-I"可以不用设定.
当动态函数库在系统路径下时,就不再需要改变环境变量LD_LIBRARY_PATH了.

3、动态函数库在系统路径下,而头文件在当前路径下:
步骤1:重复执行1、中的步骤1、2
步骤2:将动态函数库放在系统路径下/lib或/usr/lib
将头文件仍然放在当前路径下.
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test2.c test3.c test.c test.h libtest.so
[root@loalhost lishuai]#mv libtest.so /lib
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test2.c test3.c test.c test.h
步骤3:将主函数与动态函数库链接生成可执行文件
在步骤1中已经创建了动态函数库,且已经创建了需要该动态函数库的主函数.因此,下面就需要生成可执行文件了.
[root@loalhost lishuai]#gcc test.c -o test.elf -ltest

[root@loalhost lishuai]#gcc test.c -o test.elf -ltest -I./
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test2.c test3.c test.c test.elf test.h
[root@loalhost lishuai]#./test.elf
this is a test1!
this is a test2!
this is a test3!
注释:
无论是动态函数库还是静态函数库,对于头文件的位置要求并不严格,这里对参数"-I"可以不用设定.

4、头文件在系统路径下,而动态函数库在当前路径下:
步骤1:重复执行1、中的步骤1、2
步骤2:将头文件放在系统路径下/usr/include
将动态函数库仍然放在当前路径下.
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test2.c test3.c test.c test.h libtest.so
[root@loalhost lishuai]#mv test.h /usr/include
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test2.c test3.c test.c libtest.so
步骤3:将主函数与动态函数库链接生成可执行文件
在步骤1中已经创建了动态函数库,且已经创建了需要该动态函数库的主函数.因此,下面就需要生成可执行文件了.
[root@loalhost lishuai]#gcc test.c -o test.elf -L./ -ltest -I/usr/include
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#libtest.so test1.c test2.c test3.c test.c test.elf
[root@loalhost lishuai]#export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH
[root@loalhost lishuai]#./test.elf
this is a test1!
this is a test2!
this is a test3!

七、动态函数库综述
1.制作动态函数库时,其格式是:gcc 动态函数库相关.c文件(不包含.h文件) -shared -o libxxx.so
注释:
动态函数库是以二进制形式存在,也是.o文件.
由于用户将test1.c、test2.c、test3.c都编译成动态函数库,因此在"动态函数库相关.c文件(不包含.h文件)"中需要将这3个文件都列出,但需要注意的是,不能包括头文件.
当gcc编译动态函数库时,必须加上参数-shared.
动态函数库名的格式是libxxx.so,中间的xxx是动态函数库真正的名字.
2.链接动态函数库时,gcc主要用到了3个参数,分别是-L、-l、-I.
-L DIR :指定额外的动态库函数搜索路径DIR.当用户不指定动态函数库的路径时,系统会自动在系统路径下查找.
-l LIBRARY :指定链接时需要的库函数名字.这个容易理解,使用参数-L指定了库函数路径,则参数-l就指定了该路径下库函数的名字.不过需要特别注意的是,在制作动态库函数时,函数库名字必须写成libxxx.so的形式,其中,只有xxx是函数库的真实名字,所以参数-l后接函数库的真实名字,即-lxxx,而不是-llibxxx.so.
-I DIR :指定额外的头文件搜索路径.无论是动态函数库还是静态函数库,对于头文件的位置要求并不严格,这里对参数"-I"可以不用设定.

八、创建静态库函数及gcc的编译、链接
执行可执行文件时,用户必须明确静态函数库和头文件的路径.在Linux中,这种路径可分为系统路径和当前路径.因此,就会有4种情况产生,1是静态函数库和头文件均在当前路径下;2是静态函数库和头文件均在系统路径下.3是静态函数库在系统路径下,头文件在当前路径下;4是静态函数库在当前路径下,头文件在系统路径下.下面就这4中情况分别进行说明.
1、静态库函数和头文件都在当前路径下:
步骤1:创建静态库函数
创建一个头文件:test.h
创建三个.c文件:test1.c、test2.c、test3.c
将这4个文件编译成一个静态库:libtest.a
(1)使用vim编辑器分别创建这4个文件
test.h :
#include
#include
extern void test1(void);
extern void test2(void);
extern void test3(void);

test1.c
#include "test.h"
void test1(void)
{
printf("this is a test1 program!/n");
}

test2.c
#include "test.h"
void test2(void)
{
printf("this is a test2 program!/n");
}

test3.c
#include "test.h"
void test3(void)
{
printf("this is a test3 program!/n");
}
步骤2:使用gcc创建静态函数库
[root@loalhost lishuai]#gcc -c test1.c -o test1.o
[root@loalhost lishuai]#gcc -c test2.c -o test2.o
[root@loalhost lishuai]#gcc -c test3.c -o test3.o
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test1.o test2.c test2.o test3.c test3.o test.c test.h
[root@loalhost lishuai]#ar rc libtest_static.a test1.o test2.o test3.o
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#libtest_static.a test1.c test1.o test2.c test2.o test3.c test3.o test.c test.h
这样就成功创建了静态函数库libtest_static.a
注释:
libtest_static.a : 这是静态函数库名所规定的书写格式.以"lib"开头,以"a"结尾,中间是静态函数库名.
制作静态链接库时,gcc不需要参数-L、-l、-I,这些参数只是在链接函数库时才使用,而不是在制作函数库时使用.
制作静态函数库的方法:
1.分别编译函数库中包含的各个文件,并将其编译成.o文件.如,静态函数库中包含了test1.c、test2.c、test3.c,需要使用gcc将其分别编译成.o文件.
2.再用ar rc将所有的.o文件制作成静态函数库.
其中,-c是create的意思,-r是replace的意思,表示当插入的函数库名已经在库中存在,则替换同名的库.ar是显示错误信息.
步骤3:创建需要链接静态函数库的主函数
在步骤2中已经成功生成了一个用户自己的静态链接库libtest_static.a,下面通过一个程序来调用这个库里的函数.程序的源文件为:test.c
test.c :
#include "test.h"
int main(void)
{
test1();
test2();
test3();
return 0;
}
步骤4:将主函数与静态函数库一起链接生成可执行文件.
在步骤2中已经创建了静态函数库.
在步骤3中已经创建了需要该静态函数库的主函数.
因此,下面就需要生成可执行文件了.
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#libtest_static.a test1.c test1.o test2.c test2.o test3.c test3.o test.c test.h
[root@loalhost lishuai]#gcc test.c libtest_static.a -o test.elf
或者:
[root@loalhost lishuai]#gcc test.c -L./ -ltest_static -o test.elf
[root@loalhost lishuai]#./test.elf
this is a test1!
this is a test2!
this is a test3!
注释:
上面的例程中有两种生成test.elf的方法.
在方法一中,指明了生成可执行文件所需要的两个文件,这两个文件分别是主函数test.c和静态函数库libtest_static.a;
在方法二中,仅指明了生成可执行文件的主函数test.c,而静态函数库是用路径、函数库名来表示的,这种方法适用性更广.
由于制作的是静态函数库,所以就不再需要改变环境变量LD_LIBRARY_PATH了.

2、静态库函数和头文件都在系统路径下:
步骤1:重复执行1、中的步骤1、2、3
步骤2:将静态函数库放在系统路径下/lib或/usr/lib
将头文件放在系统路径下/usr/include
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#mv libtest_static.a /usr/lib
[root@loalhost lishuai]#mv test.h /usr/include
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test1.o test2.c test2.o test3.c test3.o test.c
步骤3:将主函数与静态函数库链接生成可执行文件
在步骤1中已经创建了静态函数库,且已经创建了需要该静态函数库的主函数.因此,下面就需要生成可执行文件了.
[root@loalhost lishuai]#gcc test.c -ltest_static -o test.elf
或者:
[root@loalhost lishuai]#gcc test.c -L/usr/lib -ltest_static -I/usr/include -o test.elf
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test1.o test2.c test2.o test3.c test3.o test.c test.elf
[root@loalhost lishuai]#./test.elf
this is a test1!
this is a test2!
this is a test3!
注释:由于已将静态函数库放在了系统路径下,所以,链接时不再需要定义参数"-L"了.系统会自动到相应路径下查找.但仍然需要指定该静态库函数的名字,即仍然必须定义参数"l"(有路径,也需要有该路径下函数库名).无论是动态函数库还是静态函数库,对于头文件的位置要求并不严格,这里对参数"-I"可以不用设定.

3、静态函数库在系统路径下,而头文件在当前路径下:
步骤1:重复执行1、中的步骤1、2
步骤2:将静态函数库放在系统路径下/lib或/usr/lib
将头文件仍然放在当前路径下.
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test1.o test2.c test2.o test3.c test3.o test.c test.h libtest.so
[root@loalhost lishuai]#mv libtest.so /usr/lib
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test1.o test2.c test2.o test3.c test3.o test.c test.h
步骤3:将主函数与静态函数库链接生成可执行文件
在步骤1中已经创建了静态函数库,且已经创建了需要该静态函数库的主函数.因此,下面就需要生成可执行文件了.
[root@loalhost lishuai]#gcc test.c -L/usr/lib -ltest_static -o test.elf

[root@loalhost lishuai]#gcc test.c -L/usr/lib -ltest_static -I./ -o test.elf
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test2.c test3.c test.c test.elf test.h
[root@loalhost lishuai]#./test.elf
this is a test1!
this is a test2!
this is a test3!
注释:
无论是动态函数库还是静态函数库,对于头文件的位置要求并不严格,这里对参数"-I"可以不用设定.

4、头文件在系统路径下,而静态函数库在当前路径下:
步骤1:重复执行1、中的步骤1、2
步骤2:将头文件放在系统路径下/usr/include
将静态函数库仍然放在当前路径下.
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test1.o test2.c test2.o test3.c test3.o test.c test.h libtest.so
[root@loalhost lishuai]#mv test.h /usr/include
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test1.o test2.c test2.o test3.c test3.o test.c libtest.so
步骤3:将主函数与静态函数库链接生成可执行文件
在步骤1中已经创建了静态函数库,且已经创建了需要该静态函数库的主函数.因此,下面就需要生成可执行文件了.
[root@loalhost lishuai]#gcc test.c -o test.elf -L./ -ltest_static
或者
[root@loalhost lishuai]#gcc test.c -o test.elf -L./ -ltest_static -I./
[root@loalhost lishuai]#ls
[root@loalhost lishuai]#test1.c test1.o test2.c test2.o test3.c test3.o test.c libtest.so test.elf
[root@loalhost lishuai]#./test.elf
this is a test1!
this is a test2!
this is a test3!

Attention!!!
制作静态库和动态库时,.c和.h文件都必须在同一路径下.而在链接静态库和动态库时,库和头文件可以不在同一路径下.

给我留言

留言无头像?