现在的位置: 首页 > 综合 > 正文

静态链接与动态链接

2012年10月15日 ⁄ 综合 ⁄ 共 1817字 ⁄ 字号 评论关闭

        首先我们来介绍一下静态链接于动态链接的基本思想,所谓的静态链接就是将要链接的多个文件中的相似段进行合并,再进行地址的重定位,在程序运行之前就形成一个大的单独可执行的文件。所谓的动态链接就是把程序按照模块拆分成各个相对独立的部分,在程序运行时才将他们链接在一起形成一个完整的程序,而不是像静态链接一样把所有的程序模块都链接成一个单独可执行的文件。

       在动态链接的实现中涉及到一个动态共享对象的问题,这与操作系统的平台有关。在Linux系统中,ELF动态链接文件被称为动态共享对象(DSO, Dynamic SharedObjects),简称共享对象,它们一般都以“.so”为扩展名的一些文件;而在Windows系统中,动态链接文件被称为动态链接库(Dynamical LinkingLibarry),它们通常就是我们平时很常见的以“.dll”为扩展名的文件。在动态链接方式的文件中,动态链接就是把链接这个过程从本来的程序装载前被推迟到了装载的时候才进行。

请参看如下代码示例:

/*Program1.c */
#include "Lib.h"

int main()
{
    foobar(1) ;
    return 0 ;
}

/*Program2.c */
#include "Lib.h"

int main()
{
    foobar(2) ;
    return 0 ;
}

/*Lib.c*/
#include <cstdio>

void foobar(int i)
{
    printf("Printing from Lib.so %d\n", i) ;
}

/*Lib.h*/
#ifndef LIB_H
#define LIB_H

void foobar(int i) ;

#endif

      这个程序很简单,两个程序的主要模块Program1.c 和Program2.c分别调用了Lib.c里面的foobar()函数。为了对Lib.c进行动态链接,我们把Lib.c编译成为一个共享对象文件(本文的所有操作均在linux下进行):

gcc–fPIC –shared –o Lib.so Lib.c

      经过编译之后,我们就会得到一个Lib.so文件,这就是包含了Lib.c的foobar()函数的共享对象文件,接下来我们分别编译Program1.c 和 Program2.c:

gcc–o Program1 Program1.c ./Lib.so

gcc–o Program2 Program2.c ./Lib.so

     Porgram1.c被编译成Program1.o之后,被链接成为可执行程序Program1.那么细心的朋友不禁要问,这和静态链接到底会有什么区别呢?他们的不同点就发生在Program1.o被链接成为可执行文件的这一步。在静态链接方式中,这一步链接过程会把Program1.o和Lib.o链接到一起,并且最终产生输出可执行文件Program1。但是在动态链接方式中,Lib.o并没有被链接进来,链接的输入目标文件只有Program1.o(当然还包括C语言的运行库)。但是在编译的时候使用到了共享对象文件Lib.so呀,这到底是怎么回事儿呢?它难道不参与链接么?它当然会参与链接,只不过这个链接的时期被推迟到装载的过程中。当链接器将Program1.o链接成可执行文件时,这时候链接器必须确定Program1.o中所引用的foobar()函数的性质(在Program1中,在Program1.c被编译成为Program.o时,编译器是不知道函数foobar()的地址的)。如果foobar()是一个定义于其他目标模块中的函数,那么链接器将会按照静态链接的规则,将Program1.o中的foobar()中的foobar()地址引用重定位;如果foobar()是一个定义在某个动态共享对象中的函数,那么链接器就会将这个符号的引用标记为一个动态链接的符号,不对它进行地址的重定位,把这个过程推迟到装载的时候再进行。

      那么现在的问题就是:链接器是如何知道foobar()的引用是一个静态符号还是一个动态符号呢?这就是我们在编译的时候要带上参数Lib.so的原因。Lib.so中保存了完整地 符号信息,所以把Lib.so也作为链接的输入文件之一,链接器在解析符号时就可以知道:foobar()是一个定义在Lib.so的动态符号。这样链接器就可以对foobar()的引用做特殊处理,使它成为一个对动态符号的引用。

参考书目:《程序员的自我修养》

抱歉!评论已关闭.