Linux 动态链接与静态链接

库是写好的、可复用的代码。现实中每个程序都要依赖很多底层库,不可能从零开始。本质上库是一种可执行代码的二进制形式,可被操作系统载入内存执行。库有两种:静态库(.a/.lib)和动态库(.so/.dll)——Windows 上是 .lib/.dll,Linux 上是 .a/.so。

先介绍 Linux 下 gcc 编译的几个选项:

g++ -c hellospeak.cpp

-c 表示编译源代码但不链接,输出为对象文件(默认与源码同名、后缀改为 .o),上面命令生成 hellospeak.o。下面这条命令把两个源码文件编译链接成单一可执行程序:

g++ hellospeak.cpp speak.cpp -o hellospeak

不加 -o 时编译器默认输出 a.out。所谓”静态/动态”,指的就是链接方式。

静态库

之所以叫”静态库”,是因为在链接阶段,汇编生成的目标文件 .o 与引用到的库一起打包进可执行文件,这种链接方式称为静态链接。静态库可以看作一组目标文件(.o/.obj)的集合,即很多目标文件压缩打包后的文件。特点:

  • 静态库对函数库的链接在编译期完成;
  • 程序运行时与函数库再无瓜葛,移植方便;
  • 浪费空间和资源——所有相关目标文件与函数库被链接成一个可执行文件。

命名规则

Linux 静态库命名规范为 lib[your_library_name].alib 为前缀,中间是库名,扩展名为 .a

创建静态库

  1. 将代码文件编译成目标文件 .o:
g++ -c StaticMath.cpp

(注意带 -c,否则直接编译为可执行文件)

  1. ar 工具将目标文件打包成 .a 静态库:
ar -crv libstaticmath.a StaticMath.o

生成静态库 libstaticmath.a

动态库

静态库易用、能复用代码,为什么还需要动态库?

  • 空间浪费是静态库的一个问题。

另一个问题是静态库给程序的更新、部署和发布带来麻烦:如果静态库 liba 更新了,所有使用它的应用程序都要重新编译发布(对用户而言,很小的改动就要全量重新下载)。

动态库在程序编译时并不连接到目标代码,而是在运行时才载入。不同应用程序调用相同的库,内存里只需要一份共享库实例,规避了空间浪费;运行时才载入,也解决了更新部署的麻烦——用户只需增量更新动态库。特点:

  • 动态库把对库函数的链接推迟到程序运行期;
  • 可实现进程间资源共享(因此动态库也称共享库);
  • 程序升级更简单;
  • 甚至可做到链接载入完全由程序员在代码中控制(显式调用)。

Windows 与 Linux 的差异

  • Windows 执行文件格式是 PE,动态库需要一个 DllMain 函数作初始化入口,导出函数声明通常需要 _declspec(dllexport)
  • Linux 下 gcc 默认编译 ELF 格式,不需要初始化入口,也不需要特别声明函数,编写更方便。

与创建静态库不同,创建动态库不需要打包工具(ar、lib.exe),直接用编译器即可。


   转载规则


《Linux 动态链接与静态链接》 吴杭沉 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
多线程写数组的设计方案 多线程写数组的设计方案
多线程环境下向数组添加数据的结构时,我们需要考虑线程安全。一般有几种常见的设计方案: 方案 1:互斥锁保护动态数组实现思路:使用互斥锁(std::mutex)保护对数组的每次操作,确保同一时间只有一个线程修改数组。这个方案是最简单最常见的,
2020-06-27
下一篇 
C++ 死锁排查:Shell + GDB 定位 C++ 死锁排查:Shell + GDB 定位
在 Linux 环境下进行 C++ 编程时,多线程能显著提升程序的并发处理能力,让程序在面对复杂任务时表现得更加高效。但多线程编程并非一帆风顺,死锁问题就像隐藏在暗处的”杀手”,随时可能让程序陷入僵局。 想象一下,你的程序原本运行得好好的,
2020-06-15
  目录