• 静态库与动态库的编译链接


      一.静态库和动态库的简单介绍

      程序设计的模块化是人们一直在追求的目标,因为当一个系统十分复杂的时候,将系统模块化既可以并行开发,又可以增强程序的可用性,降低程序间的耦合度。在一个复杂的多模块系统中,

    各个模块编译完成后,会生成各自的目标文件*.o,最后通过链接器将各个模块链接起来生成可执行文件。

             库其实就是一个模块文件。人们为了将一些功能模块提供给他人使用,同时又不想将源代码直接分发给别人(也可能是不需要,毕竟库使用更方便,不用重新编译),就将功能模块做成库,

    外部应用通过链接库来加载库的功能模块。比如glibc是GNU标准的C标准函数库。

             库有静态和动态之分。静态库在编译时被链接进可执行文件中,动态库是在程序运行时链接。静态库的优点是使用方便,只要编译时链接成功,在程序运行时就不会有找不到库或者库错乱的

    问题,但是这也造成了升级更新困难和内存空间浪费的问题。对于静态库来说,升级就必须重新编译应用程序链接静态库然后全部升级,而且如果多个应用程序都用到了同一个静态库,那么当多个

    应用程序运行时,内存中就会有静态库的多个拷贝,十分浪费空间。因为这些原因,动态库应运而生。动态库是程序运行时才链接,所以在程序更新时,我们只需要更新动态库文件,重新启动应用

    程序就会链接新库,而且当内存中已经有一个动态库的拷贝时,其他应用在运行时,如果需要链接库,会先从内存中查找库,找到后就直接链接该库,找不到再加载库到内存中,这保证了不会有同

    一个库的多个拷贝在内存中占用空间。

      二.静态库和动态库的编译和链接 

      2.1 程序源码

      我们将会创建一个app,主要功能是输出“hello world”,具体实现在库libhello.a/libhello.so中实现。下面是各个文件的源代码:

          

      app源码

      #include<stdlib.h>

      #include<stdio.h>

      #include"hello.h"

      void main(void)

      {

              hello();

              hello();

      }

      

      库源码

      

      hello.c源码

      #include<stdlib.h>

      #include<stdio.h>

      void hello(void)

      {

              printf(" ======hello world====== ");

      }

      hello.h源码

      #ifndef HELLO_H

      #define HELLO_H

      void hello(void);

       #endif

      2.2.静态库的编译和链接

      

      第一步:生成目标文件

        # gcc -c hello.c

        生成目标文件hello.o

      第二步:编译生成静态链接库libhello.a

        # ar rcs libhello.a hello.o

      第三步:增加用于外部调用静态库函数的头文件hello.h

        hello.h头文件的作用是给外部调用库函数提供函数声明

      第四步:外部程序使用静态库

        在app.c中包含所用静态库函数声明的头文件hello.h

        生成app.o的目标文件

        #gcc -c app.c

        链接静态库libhello.a生成可执行文件app

        #gcc -o app app.o -L. -lhello

        执行app

        #./app

          ======hello world======

          ======hello world======

      2.3.动态库的生成

      同样是先生成目标文件hello.o,然后编译动态库文件libhello.so

      # gcc -shared -fPCI -o libhello.so hello.o

      # ls

      发现libhello.so已经生成了,然后我们开始生成可执行文件。这里动态库并没有链接。

      # gcc -o app app.o  -L. -lhello

      编译通过,执行app时出错

      #./app

      ./app: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory

      程序运行时,找不到动态库。那为什么编译时能通过呢?因为编译时的-L指定了编译路径,所以编译能通过,但是在运行加载动态库时,会默认从/lib,/usr/lib路径下去找,而实际库不在该路径下,所以加载失败。

      

      解决的办法有三个:

      1.将库文件拷贝到默认的动态库路径/lib,/usr/lib,此为最简单,最快捷的方法

      2.程序编译时指定动态库路径,使用“-Wl,-rpath”。

        # gcc -o app app.o  -L. -lhello -Wl,-rpath=/mnt/hgfs/share/workspace/demo/LibraryDemo1

      注意:有的系统中可能是"-Wl,-rpath,/path/to/dir",使用时需要注意。还有就是我自己在使用时出现的小问题,提醒下大家,我开始总是写成-Wl,rpath=path,结果编译怎么都不过,总是提示找不打rpath=path这个文件或路径,后来才发现少写了个“-”,提醒大家需要认真看错误提示。

         3.就是更改/etc/ld.so.conf。查看/etc/ld.so.conf发现它包含了/etc/ld.so.conf.d目录下的所有*.conf。到/etc/ld.so.conf.d/目录下,

       新建一个*.conf,在里面加上动态库的路径(你也可以直接在其中某一个conf中增加一条,但是最好不要这么干,否则以后可能有未知的混乱)。

       此时运行app仍然失败,原因是系统查找动态库是通过查找/etc/ld.so.cache缓存,仅仅更改配置是不行的,还需要更新缓存。此时可以通过/sbin/ldconfig更新缓存。

       # /sbin/ldconfig

       有时候想知道动态库路径是否加入到缓存中,可以使用ldconfig指令

       # sudo /sbin/ldconfig -p |grep path

         在执行更新缓存后,可以查看到:

       # libhello.so (libc6) => /mnt/hgfs/share/workspace/demo/LibraryDemo1/libhello.so

        以上三种方法亲测可用。

  • 相关阅读:
    大牛思考方式
    web面试题大全
    github上最全的资源教程-前端涉及的所有知识体系
    java switch语句注意的事项
    Lucene Payload 的研究与应用
    hive array、map、struct使用
    黑马程序员--正则表达式
    [置顶] 读源码练内功(一):guava之eventbus
    自定义Java Annotations实例以及用Java Reflection来解析自定义的Annotation
    Solr之NamedList 简单介绍与实例解析
  • 原文地址:https://www.cnblogs.com/Victor-Tian/p/7258729.html
Copyright © 2020-2023  润新知