现在的位置: 首页 > 综合 > 正文

静态链接库和动态链接库

2018年04月17日 ⁄ 综合 ⁄ 共 14309字 ⁄ 字号 评论关闭
      库,是一种封装机制,简单说是把所有的源代码编译成目标代码后打成的包.库的开发者除了提供库的目标代码外,还提供一系列的头文件,头文件中就包含了库的接口,另外还有一些必要的注释.库函数根据是否被编译到程序内部而分为静态链接库(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 <stdio.h>
          #include <stdlib.h>
          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
          注释:
          <1> -shared    : 创建动态函数库
             libtest.so : 这是动态函数库名所规定的书写格式.以"lib"开头,以"so"结尾,中间是动态函数库名.
          <2> 制作动态函数库时,不需要加上头文件,即:
              不能写成:gcc test1.c test2.c test3.c test.h -shared -o libtest.so
          <3> 制作动态链接库时,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!
        注释:<1>由于已将动态函数库放在了系统路径下,所以,链接时不再需要定义参数"-L"了.系统会自动到相应路径下查找.但仍然需要指定该动态库函数的名字,即仍然必须定义参数"l"(有路径,也需要有该路径下函数库名).无论是动态函数库还是静态函数库,对于头文件的位置要求并不严格,这里对参数"-I"可以不用设定.
           <2>当动态函数库在系统路径下时,就不再需要改变环境变量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
      注释:
      <1>动态函数库是以二进制形式存在,也是.o文件.
      <2>由于用户将test1.c、test2.c、test3.c都编译成动态函数库,因此在"动态函数库相关.c文件(不包含.h文件)"中需要将这3个文件都列出,但需要注意的是,不能包括头文件.
      <3>当gcc编译动态函数库时,必须加上参数-shared.
      <4>动态函数库名的格式是libxxx.so,中间的xxx是动态函数库真正的名字.
     2.链接动态函数库时,gcc主要用到了3个参数,分别是-L、-l、-I.
      <1>-L DIR     :指定额外的动态库函数搜索路径DIR.当用户不指定动态函数库的路径时,系统会自动在系统路径下查找.
      <2>-l LIBRARY :指定链接时需要的库函数名字.这个容易理解,使用参数-L指定了库函数路径,则参数-l就指定了该路径下库函数的名字.不过需要特别注意的是,在制作动态库函数时,函数库名字必须写成libxxx.so的形式,其中,只有xxx是函数库的真实名字,所以参数-l后接函数库的真实名字,即-lxxx,而不是-llibxxx.so.
      <3>-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 <stdio.h>
        #include <stdlib.h>
        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
        注释:
        <1> libtest_static.a : 这是静态函数库名所规定的书写格式.以"lib"开头,以"a"结尾,中间是静态函数库名.
        <2> 制作静态链接库时,gcc不需要参数-L、-l、-I,这些参数只是在链接函数库时才使用,而不是在制作函数库时使用.
        <3> 制作静态函数库的方法:
            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!
       注释:
       <1>上面的例程中有两种生成test.elf的方法.
          在方法一中,指明了生成可执行文件所需要的两个文件,这两个文件分别是主函数test.c和静态函数库libtest_static.a;
          在方法二中,仅指明了生成可执行文件的主函数test.c,而静态函数库是用路径、函数库名来表示的,这种方法适用性更广.
       <2>由于制作的是静态函数库,所以就不再需要改变环境变量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文件都必须在同一路径下.而在链接静态库和动态库时,库和头文件可以不在同一路径下. 
【上篇】
【下篇】

抱歉!评论已关闭.