提要:

1.静态库        

        静态库,也称作归档库(archive),按惯例它们的文件名都以.a结尾。比如,标准C语言函数库/usr/lib/libc.aX11函数库/usr/X11/lib/libX11.a

2.共享库

        静态库的一个缺点,当同时运行许多应用程序并且它们都使用来自同一个函数库的函数时,就会在内存中有同一函数的多份拷贝,在程序文件自身中也有多份同样的拷贝。这将消耗大量宝贵的内存和磁盘空间。可以用共享库来实现函数的动态链接。

        Linux支持共享库(动态链接库)。共享库的保存位置与静态库是一样的,但共享库有不同的文件名后缀。在典型的Linux系统中,标准数学库的共享库是/usr/lib/libm.so

        程序使用共享库时,它的链接方式是这样的:它本身不再包含函数代码,而是运行时可访问的共享代码。当编译好的程序被装载到内存中执行时,函数引用被解析并产生对共享库的调用,如果有必要共享库才被加载到内存中。

前言

我们在编写代码的时候经常用到已有的接口,他们是以库的形式提供给我们使用的,而常见形式有两种,一种常以.a为后缀,为静态库;另一种以.so为后缀,为动态库。那么这两种库有什么区别呢?

说明:本文主要说明Linux下的情况,windows不涉及。

目标文件

在解释静态库和动态库之前,需要简单了解一下什么是目标文件。目标文件常常按照特定格式来组织,在linux下,它是ELF格式(Executable Linkable Format,可执行可链接格式),而在windows下是PE(Portable Executable,可移植可执行)。

而通常目标文件有三种形式:

  • 可执行目标文件。即我们通常所认识的,可直接运行的二进制文件。
  • 可重定位目标文件。包含了二进制的代码和数据,可以与其他可重定位目标文件合并,并创建一个可执行目标文件。
  • 共享目标文件。它是一种在加载或者运行时进行链接的特殊可重定位目标文件。

我们来看一个简单实例:

//main.c
#include<stdio.h>
#include<math.h>
int main(int argc,char *argv[])
{printf("hello 编程珠玑\n");int b = 2;double a = exp(b);printf("%lf\n",a);return 0;
}

代码计算e的2次方并打印结果。由于代码中用到了exp函数,它位于数学库libm.so或者libm.a中,因此编译时需要加上-lm。

生成可重定位目标文件main.o:

$ gcc -c main.c   #生成可重定位目标文件
$ readelf -h main.o  #查看elf文件头部信息
ELF Header:Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class:                             ELF64Data:                              2's complement, little endianVersion:                           1 (current)OS/ABI:                            UNIX - System VABI Version:                       0Type:                              REL (Relocatable file)
(省略其他内容)

通过上面的命令将main.c生成为可重定位目标文件。通过readelf命令也可以看出来:REL (Relocatable file)。

观察共享目标文件libm.so:

$ readelf -h /lib/x86_64-linux-gnu/libm.so.6
ELF Header:Magic:   7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00 Class:                             ELF64Data:                              2's complement, little endianVersion:                           1 (current)OS/ABI:                            UNIX - GNUABI Version:                       0Type:                              DYN (Shared object file)
(省略其他内容)

不同系统中libm.so的位置可能不一样,你可以通过locate命令来查找。locate命令的用法可参考《Linux中的文件查找技巧》。从结果可以看到,libm.so是共享目标文件(Shared object file)。

查看可执行目标文件main:

$ gcc -o main main.o -lm  #编译成最终的可执行文件
$ readelf -h main         #查看ELF文件头
ELF Header:Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class:                             ELF64Data:                              2's complement, little endianVersion:                           1 (current)OS/ABI:                            UNIX - System VABI Version:                       0Type:                              EXEC (Executable file)
(省略其他内容)

这里必须要强调一点,如果使用到的函数没有在libc库中,那么你就需要指定要链接的库,本文中需要链接libm.so或libm.a。可以看到,最终生成的main类型是Executable file,即可执行目标文件。

什么是静态库

前面所提到可重定位目标文件以一种特定的方式打包成一个单独的文件,并且在链接生成可执行文件时,从这个单独的文件中“拷贝”它自己需要的内容到最终的可执行文件中。这个单独的文件,称为静态库。linux中通常以.a(archive)为后缀

还是拿前面的例子来说,我们使用静态链接构建我们的可执行文件:

$ gcc -c main.c
$ gcc -static -o main main.o -lm

在这个过程中,就会用到系统中的静态库libm.a。这个过程做了什么呢?首先第一条命令会将main.c编译成可重定位目标文件main.o,第二条命令的static参数,告诉链接器应该使用静态链接,-lm参数表明链接libm.a这个库(类似的,如果要链接libxxx.a,使用-lxxx即可)。由于main.c中使用了libm.a中的exp函数,因此链接时,会将libm.a中需要的代码“拷贝”到最终的可执行文件main中。

特别注意,必须把-lm放在后面。放在最后时它是这样的一个解析过程:

  • 链接器从左往右扫描可重定位目标文件和静态库
  • 扫描main.o时,发现一个未解析的符号exp,记住这个未解析的符号
  • 扫描libm.a,找到了前面未解析的符号,因此提取相关代码
  • 最终没有任何未解析的符号,编译链接完成

那如果将-lm放在前面,又是怎样的情况呢?

  • 链接器从左往右扫描可重定位目标文件和静态库
  • 扫描libm.a,由于前面没有任何未解析的符号,因此不会提取任何代码
  • 扫描main.o,发现未解析的符号exp
  • 扫描结束,还有一个未解析的符号,因此编译链接报错

如果把-lm放在前面,编译结果如下:

$ gcc -static -lm -o main main.o
main.o: In function `main':
main.c:(.text+0x2f): undefined reference to `exp'
collect2: error: ld returned 1 exit status

更详细的解释也可以参考《一个奇怪的链接问题》。

我们看看最终生成的文件大小:

$ ls -lh main
-rwxrwxr-x 1 hyb hyb 988K 6月  27 20:22 main

生成的可执行文件大小为988k。ls的高级用法可参考《ls命令常见实用用法》。

由于最终生成的可执行文件中已经包含了exp相关的二进制代码,因此这个可执行文件在一个没有libm.a的linux系统中也能正常运行。

什么是动态库

动态库和静态库类似,但是它并不在链接时将需要的二进制代码都“拷贝”到可执行文件中,而是仅仅“拷贝”一些重定位和符号表信息,这些信息可以在程序运行时完成真正的链接过程。linux中通常以.so(shared object)作为后缀。

通常我们编译的程序默认就是实用动态链接:

$ gcc -o main main.c -lm  #默认使用的是动态链接

我们来看最终生成的文件大小:

$ ls -lh main
-rwxrwxr-x 1 hyb hyb 8.5K 6月  27 20:25 main

可以看到,通过动态链接的程序只有8.5k

另外我们还可以通过ldd命令来观察可执行文件链接了哪些动态库:

$ ldd mainlinux-vdso.so.1 =>  (0x00007ffc7b5a2000)libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe9642bf000)libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe963ef5000)/lib64/ld-linux-x86-64.so.2 (0x00007fe9645c8000)

正因为我们并没有把libm.so中的二进制代码“拷贝”可执行文件中,我们的程序在其他没有上面的动态库时,将无法正常运行。

有什么区别

到这里我们大致了解了静态库和动态库的区别了,静态库被使用目标代码最终和可执行文件在一起(它只会有自己用到的),而动态库与它相反,它的目标代码在运行时或者加载时链接。正是由于这个区别,会导致下面所介绍的这些区别。

可执行文件大小不一样

从前面也可以观察到,静态链接的可执行文件要比动态链接的可执行文件要大得多,因为它将需要用到的代码从二进制文件中“拷贝”了一份,而动态库仅仅是复制了一些重定位和符号表信息。

占用磁盘大小不一样

如果有多个可执行文件,那么静态库中的同一个函数的代码就会被复制多份,而动态库只有一份,因此使用静态库占用的磁盘空间相对比动态库要大。

扩展性与兼容性不一样

如果静态库中某个函数的实现变了,那么可执行文件必须重新编译,而对于动态链接生成的可执行文件,只需要更新动态库本身即可,不需要重新编译可执行文件。正因如此,使用动态库的程序方便升级和部署。

依赖不一样

静态链接的可执行文件不需要依赖其他的内容即可运行,而动态链接的可执行文件必须依赖动态库的存在。所以如果你在安装一些软件的时候,提示某个动态库不存在的时候也就不奇怪了。

即便如此,系统中一班存在一些大量公用的库,所以使用动态库并不会有什么问题。

复杂性不一样

相对来讲,动态库的处理要比静态库要复杂,例如,如何在运行时确定地址?多个进程如何共享一个动态库?当然,作为调用者我们不需要关注。另外动态库版本的管理也是一项技术活。这也不在本文的讨论范围。

加载速度不一样

由于静态库在链接时就和可执行文件在一块了,而动态库在加载或者运行时才链接,因此,对于同样的程序,静态链接的要比动态链接加载更快。所以选择静态库还是动态库是空间和时间的考量。但是通常来说,牺牲这点性能来换取程序在空间上的节省和部署的灵活性时值得的。再加上局部性原理,牺牲的性能并不多。

总结

静态库和动态库具体是何如链接的已经超出了本文的介绍范围,本文仅简单介绍了一些静态库和动态库的区别,另外文中提到的在其他的linux系统,也指的是同样处理器架构的系统。但是了解这些基本信息,就能够帮助我们解决很多编译问题了。更多内容可自己阅读装载,链接方面的书籍。后面的文章也会介绍更多相关信息。

本文相关阅读

  • 《ls命令常见实用用法》
  • 《linux常用命令-开发调试篇》
  • 《一个奇怪的链接问题》
  • 《Linux中的文件查找技巧》

参考书籍

  • 《程序员的自我修养》
  • 《深入理解计算机系统》

原文地址静态库和动态库

微信公众号【编程珠玑】:专注但不限于分享计算机编程基础,Linux,C语言,C++,数据结构与算法,工具,资源等编程相关[原创]技术文章,号内包含大量经典电子书和视频学习资源。欢迎一起交流学习,一起修炼计算机“内功”,知其然,更知其所以然。

静态库与动态库(共享库)的联系与区别相关推荐

  1. Linux下的静态库、动态库和动态加载库

    from: http://www.techug.com/linux-static-lib-dynamic-lib 库的存在极大的提高了C/C++程序的复用性,但是库对于初学者来说有些难以驾驭,本文从L ...

  2. XCTF-攻防世界CTF平台-Reverse逆向类——56、tar-tar-binks(Mac平台下的64位动态链接共享库.dylib逆向)

    目录标题 一.解压缩 二.查看文件 三.分析程序 四.程序主要逻辑: 五.逆向思路: 步骤一: 步骤二: 六.解密代码: 题目提供了两个文件flag.tar和libarchive.dylib 一.解压 ...

  3. 程序员的自我修养--链接、装载与库笔记:Linux共享库的组织

    共享库(Shared Library)概念:其实从文件结构上来讲,共享库和共享对象没什么区别,Linux下的共享库就是普通的ELF共享对象.由于共享对象可以被各个程序之间共享,所以它也就成为了库的很好 ...

  4. Linux命令把共享库export,Linux Linux共享库

    so文件在linux中为共享库,与windows下的dll类似. so文件中的函数可供多个进程调用,最大可能的提供二进制代码复用. 共享库可以使代码的维护工作大大简化,当修正了一些错误或者添加了新特性 ...

  5. dlopen动态装载共享库

    原文链接: http://www.ccccxy.top/coding/archives/2020/10/01/dlopen_load_shared_library_11/ 欢迎大神们评论指导和斧正 一 ...

  6. Linux共享库编程方法,Linux共享库c

    我有一个明智的共享图书馆.我如何在我的程序中使用它?我是否需要包含该库的标题? 我在Linux下使用Eclipce.我使用-L和-l设置了库的路径.但我的功能在程序中不可见. 你能解释一下共享图书馆是 ...

  7. linux编译静态库的头文件,条件编译,头文件,静态库,共享库与多文件编程

    条件编译 条件编译即满足某些条件的时候编译某部分代码,常用于开发多个版本的程序,当满足条件A时,编译出免费版本的软件,当满足条件B时,编译除vip版本的软件,可以提高代码的复用率.条件编译使用&quo ...

  8. linux 头文件卫士,条件编译,头文件,静态库,共享库与多文件编程

    原标题:条件编译,头文件,静态库,共享库与多文件编程 本文转载自嵌入式Linux中文站条件编译 条件编译即满足某些条件的时候编译某部分代码,常用于开发多个版本的程序,当满足条件A时,编译出免费版本的软 ...

  9. C 语言编程 — 静态库、动态库和共享库

    目录 文章目录 目录 文章目录 程序函数库 静态链接 创建静态库文件 动态链接 创建共享库文件 共享库文件的名字 共享库文件的存储路径 LD_LIBRARY_PATH 环境变量 ldconfig 指令 ...

  10. go语言调用c 的头文件 so,Golang生成共享库(shared library)以及Golang生成C可调用的动态库.so和静态库.a...

    Golang类似于C的静态语言,效率也接近于C,如果Golang也可以导出可供C调用的库,那可以和很多高级语言say goodbye了,goodbye似乎又有点武断,但至少说,Golang可以做很多事 ...

最新文章

  1. 从 Windows 切换到 Mac,不能错过这9条Tips
  2. python编程爱心-python画一个玫瑰和一个爱心
  3. proe3.0安装教程
  4. php做微信小程序登录,php(ThinkPHP)实现微信小程序的登录过程
  5. new/delete与malloc/free
  6. CentOS 利用Yum安装mysql后无法启动(MySQL Daemon failed to start.)
  7. C++ delete的三种面貌
  8. 公众号管理系统 html,微信公众号平台管理后台.html
  9. 第一个Vert.x程序
  10. Visual Studio 2017 编译Clang
  11. 游戏制作大致流程粗谈之五
  12. 区块链专利正从量向质转变 智慧金融、医疗健康和能源三领域质量最高
  13. python版js压缩工具
  14. C#下对PDF文件进行电子图片签名
  15. android sdcard下创建文件,android创建以及使用SDcard镜像文件
  16. 如何判断欠拟合、适度拟合、过拟合
  17. Matlab读取二进制数据文件
  18. 使用源码部署CITA(Ubuntu18.0.4 | VMware)
  19. 两名一流高校硕士同年毕业论文高度雷同惹争议,怎么过的查重?
  20. (简单课设)前端小白刚做的一个简单的移动端项目的分享和总结

热门文章

  1. 10分钟搞定Java带token验证的注册登录
  2. SSH简介及登录方法
  3. 科学计数法转换为普通数字
  4. 当语音遇到人工智能,走进《智能语音时代》
  5. 国内计算机类学术期刊投稿指南
  6. WASC Threat Classification 安全威胁分类
  7. 最详细的Android图片压缩解释
  8. LNK 2001错误
  9. 新浪微博定位页面代码解析
  10. python办公自动化之word表格跨页断行-AllowBreakAcrossPages