动态库与静态库

来源:互联网 发布:淘宝演唱会门票可靠吗 编辑:程序博客网 时间:2024/05/29 19:02

动态库与静态库

什么是库

  • 所谓的库就是一些大神们写好的实现某一功能的代码,我们可以拿复用,这样就可以提高开发效率。
  • 库的本质是一种二进制的文本文件,可以被操作系统载入内存执行。
  • 库又分为两种动态库(.so/.dll)和静态库(.a/.lib)

库是在什么时候被调用的

一般我们写的代码会有多个源文件,这些源文件在编译成可执行成语要经过四个步骤,再最后一步链接之前,这些源文件都是各自编译的,互不干扰,所以库和源文件是在链接的时候被加载到一起的。这里又分为静态链接和动态链接,分别调用的是静态库和动态库。

image

具体链接过程下面分别讲述

静态库

静态库就是在链接阶段把目标文件.o和调用到的静态库一起链接打包到可执行程序里。

  • 如下图:如果多个进程都调用一个静态库,那么这个库在内存中就多份拷贝导致浪费空间。

image

优点

  • 程序运行时与静态库没有瓜葛了,因为程序自己已经有一份静态库了,所以方便移植。
  • 因为不需要动态链接,不需要在符号表里面查询,所以代码装载速度快,执行速度略比动态链接库快。
  • 只需保证在开发者的计算机中有正确的.LIB文件,在以二进制形式发布程序时不需考虑在用户的计算机上.LIB文件是否存在及版本问题,可避免DLL地域等问题。

缺点

  • 静态库在程序编译时会被链接到目标文件中,因此可执行程序的体积比较大。如果同时执行多个同样的可执行程序,就意味着在内存中每一个执行程序都有一份静态库。
  • 修改起来麻烦,不方便维护

Liunx应用静态库

  • LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径

1.从上面编译过程那张图可以发现,无论是调用静态库还是动态库,都要编译到.o文件才可以链接,既然要把静态库和.o文件链接在一起,所以静态库也可以简单的看出.o文件。我们先把库文件编译成.o文件


2.由.o文件生成静态库文件

  • liunx下静态库的命名规范 "lib[name].a" lib为前缀,name是静态库名称,扩展名是.a image

3.使用静态库

gcc -o main main.c -static -lmytest -L.
  • static 表示程序是静态链接
  • -l[name] 指定静态库
  • -L[path] 指定静态库的路径 '.' 表示当前路径

image

这时我把静态库删除,依然可以运行程序

image

动态库

  • 动态库并没与被链接到可执行文件里,而是在运行时去才被载入,如何多个可执行程序调用同一个动态库,这个动态库在内存中只有一份,通过共享映射区映射给每一个调用这个库的进程,知道最后一个调用这个库的进程结束,内存中的动态库才撤出内存(引用计数)。因此也叫共享库,这也就是是静态库与动态库的区别所在,避免了静态库的空间浪费。

image

优点

  • 动态库可以同时被多个执行程序通过共享映射区共享,因此动态库在内存中只占一份,比较节省内存,他是通过引用计数来统计当前链接的执行程序,链接为零就释放动态库的内存。
  • DLL文件与EXE文件独立,只要输出接口不变(即名称、参数、返回值类型和调用约定不变),更换DLL文件不会对EXE文件造成任何影响,因而极大地提高了可维护性和可扩展性;例如程序的增量更新。
  • 不同编程语言编写的程序只要按照函数调用约定就可以调用同一个DLL函数;
  • 适用于大规模的软件开发,使开发过程独立、耦合度小,便于不同开发者和开发组织之间进行开发和测试。

缺点

  • 使用动态链接库的应用程序不是自完备的,它依赖的DLL模块也要存在,如果使用载入时动态链接,程序启动时发现DLL不存在,系统将终止程序并给出错误信息。而使用运行时动态链接,系统不会终止,但由于DLL中的导出函数不可用,程序会加载失败;速度比静态链接慢。当某个模块更新后,如果新模块与旧的模块不兼容,那么那些需要该模块才能运行的软件,统统撕掉。这在早期Windows中很常见。

动态库特点总结:

  • 动态库把对一些库函数的链接载入推迟到程序运行的时期。
  • 可以实现进程之间的资源共享。(因此动态库也称为共享库)
  • 将一些程序升级变得简单。 -甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。Window与Linux执行文件格式不同,在创建动态库的时候有一些差异。
  • 在Windows系统下的执行文件格式是PE格式,动态库需要一个DllMain函数做出初始化的入口,通常在导出函数的声明时需要有_declspec(dllexport)关键字。
  • Linux下gcc编译的执行文件默认是ELF格式,不需要初始化入口,亦不需要函数做特别的声明,编写比较方便。

使用动态库

  • LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径

1.创建动态库 创建和使用动态库的命令在最下面解释

image

2.使用动态库

执行下面指令生成可执行程序,-L在这里可以不用,下面寻找动态库的方法会讲到。


image 但是发现无法运行

现在可以通过ldd命令来查看可执行程序依赖的共享库

image

发现liymytest.so 找不到,为什么这样呢?

虽然上面编译时我们使用了'-L.',但是LD_LIBRARY_PATH环境变量没有指定liymytest.so库的绝对路径,所以就按下面的方法寻找动态库:

Linux系统下,DL函数库与其他函数库在格式上没有特殊的区别,它们创 建的时候是标准的object格式。主要的区别就是这些函数库不是在程序链接的时候或者启动 的时候加载,而是通过一个API来打开一个函数库,寻找符号表,处理错误和关闭函数库。 通常C语言环境下 ,需要包含这个头文件dlfcn.h。 Linux中使用的函数和Solaris中一样,都是dlpoen()API。当时不是所有的平台都使用同 样的接口,例如HP-UX使用hl_load()机制,而Windows平台用另外的其他的调用接口。如果 你的目的是使得你的代码有很强的移植性,你应该使用一些wrapping函数库,这样的wrappi ng函数库隐藏 不同的平台的接口区别。一种方法是使用glibc函数库中的对动态加载模块的支持,它使用 一些潜在的动态加载函数库界面使得它们可以夸平台使用。 dlopen函数打开一个函数库然后为后面的使用做准备。

C语言原形是:

void * dlopen(const char *filename, int flag);

如果文件名filename是以“/”开头,也就是使用绝对路径,那么dlopne就直接使用它,而不去查找某些环境变量或者系统设置的函数库所在的目录了。否则dlopen()就会按照下面的次序查找函数库文件:

  1. 环境变量LD_LIBRARY指明的路径。该环境变量主要用于指定查找共享库(动态链接库)时除了默认路径之外的其他路径。可以在编译时用-L指定路径来寻找动态库。

  2. /etc/ld.so.cache中的函数库列表。 如果安装在其他目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下:
    i.编辑/etc/ld.so.conf文件,加入库文件所在目录的路径
    ii.运行ldconfig ,该命令会重建/etc/ld.so.cache文件

  3. /lib目录,然后/usr/lib。不过一些很老的a.out的loader则是采用相反的次序,也就是 先查/usr/lib,然后是/lib。如果安装在/lib或者/usr/lib下,那么ldd默认能够找到,无需其他操作。

这里我使用第一种办法把动态库移动到/uer/lib下,即可运行

image

g++(gcc)编译选项

  • -shared :指定生成动态链接库。
  • -static :指定生成静态链接库。
  • -fPIC :表示编译为位置独立的代码,用于编译共享库。目标文件需要创建成位置无关码, 念上就是在可执行程序装载它们的时候,它们可以放在可执行程序的内存里的任何地方。
  • -L. :表示要连接的库所在的目录。
  • -l:指定链接时需要的动态库。编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.a/.so来确定库的名称。
  • -Wall :生成所有警告信息。
  • -ggdb :此选项将尽可能的生成gdb 的可以使用的调试信息。
  • -g :编译器在编译的时候产生调试信息。
  • -c :只激活预处理、编译和汇编,也就是把程序做成目标文件(.o文件) 。
  • -Wl,options :把参数(options)传递给链接器ld 。如果options 中间有逗号,就将options分成多个选项,然后传递给链接程序。

nm命令查符号表

有时候可能需要查看一个库中到底有哪些函数,nm命令可以打印出库中的涉及到的所有符号。库既可以是静态的也可以是动态的。nm列出的符号有很多,常见的有三种:

  • 一种是在库中被调用,但并没有在库中定义(表明需要其他库支持),用U表示;
  • 一种是库中定义的函数,用T表示,这是最常见的;
  • 一种是所谓的弱态”符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示。 $nm libhello.h

windows下使用动态库和静态库方法:http://developer.51cto.com/art/201505/476344.htm


image