23 October 2013

要了解链接,首先要知道编译系统是怎么回事,这里拿GCC为例,GCC的编译系统如图:

  • 预处理部分一般是将头文件及宏展开。
  • 编译阶段将hello.i文件翻译成汇编文件。
  • 汇编阶段将汇编文件翻译为二进制的可重定位目标程序的格式。
  • 链接阶段将可重定位目标程序和一些库文件打包在一起,成为可执行文件,该文件可加载至内存运行。

链接即为第四阶段。它的两个重要任务是:符号解析和重定位。符号解析的目的是将每个符号引用刚好和一个符号定义链接起来。重定位把每个符号定义与一个存储器的位置联系起来,然后修改所有对这些符号的引用,使得它们指向这个存储器位置。链接器解析符号用的方法为将每个引用与它输入的可重定位目标文件的符号表中的一个确定的符号定义联系起来。符号解析完成后,链接器就知道它的输入目标模块代码中的代码节和数据节的确切大小,然后进行重定位。

这里我们需要了解一个概念——目标文件。目标文件分为三种:可重定位目标文件(汇编阶段生成)、可执行目标文件(a.out,有关其为何命名为a.out请查阅《C专家编程》)以及共享目标文件。目标文件的格式对于不同系统有所不同,我们只讨论Unix下的ELF文件(可执行和可链接格式)。典型的ELF可重定位目标文件如图:

本期的主角是链接,这个可重定位目标文件如此复杂,在此不做一一介绍,总之,链接器要解析这个文件。完成符号解析和重定位,再生成可执行文件就大功告成了。

我们经常看到的链接库(unix)链接有两种:静态库链接(.a)和动态库链接(.so)。其主要区别在于加载库文件的时间是编译阶段还是运行阶段。静态链接库的一份拷贝是可执行文件的物理组成部分。而动态链接库只在程序即将运行时进行加载。

似乎动态链接库优点多多,没错,动态链接库是一种非常现代化的方法:

1 动态链接库可执行文件的体积更小。这个容易理解,静态链接库把所有的静态库都加到了可执行文件,就像一个大杂烩,不管你喜不喜欢吃(功能是否使用的到),全都要加到一起。动态库连接只有需要时才被映射到进程中,不仅使可执行文件体积更小,而且根据不同的需要仅加载需要的部分。

2 动态链接库的可执行文件运行时共享该函数库的一个单独拷贝。内核保证映射到内存中的函数库可以被所有使用它们的进程共享,这就提供了更好的I/0和交换空间利用率,节省了物理内存,从而提高了系统性能。对于静态链接库,每个文件都要包含一份函数库的拷贝,极为浪费。

3 动态链接库允许用户在运行时选择需要执行的函数库。用户可以在程序执行时用一个库文件取代另一个库文件,使得函数库的版本升级更为容易。

参考资料:



blog comments powered by Disqus