内存管理-程序的装入和链接

来源:互联网 发布:java中foreach 编辑:程序博客网 时间:2024/06/01 10:20

程序只有被转入内存才能运行,将一个源程序变成一个可在内存中执行的程序需要经历以下几个步骤:

1.编译:大多数编译系统提供编译驱动程序,它包括语言预处理器、编译器、汇编器和链接器。假如先有main.c和swap.c两个源文件,main.c中引用了swap.c的函数,编译过程如图所示:

1.1 驱动程序先调用cpp预处理器(cpp),它将C源程序main.c翻译成一个ASCII码的中间文件main.i

1.2 接下来驱动程序运行C编译器(ccl),它将main.i翻译成一个ASCII汇编语言文件main.s

1.3 然后驱动程序运行汇编器(as),它将main.s翻译成一个可重定位目标文件 main.o

2.链接:链接是把各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载到存储器执行。链接可以在以下三个阶段执行:

2.1 可以执行于编译时,也就是源代码被翻译成机器代码时。(静态链接方式)

如:三个目标模块A、B、C,它们的长度分别为L、M、N,在模块A中有一条语句CALLB,用于调用模块B。在模块B中有一条语句CALLC,用于调用模块C。B和C都属于外部调用符号,在讲这几个目标模块装配成一个装入模块时,须解决以下两个问题:

2,1,1 对相对地址进行修改。在由编译程序所产生的所有目标模块中,试用的都是相对地址,其起始地址都为0,每个模块中的地址都是相对于起始地址计算的。在链接成一个装入模块后,原模块B和C在装入模块的起始地址不再是0,而分别是L和L+M,所以此时须修改模块B和C中的相对地址,即把原B中的所有相对地址都加上L,把原C中的所有相对地址都加上L+M。

2.1.2 变换外部调用符号。将每个模块中所用的外部调用符号也都变换为相对地址,如把B的起始地址变换为L,把C的起始地址变换为L+M,如下图,这种先进行链接所形成的一个完整的装入模块,又称为可执行文件。通常都不再拆开它,要执行时可直接将它装入内存。

2.2 可以执行于加载时,也就是在程序被加载器加载到存储器并执行时。(装入时动态链接)

用户源程序经编译后所得的目标模块,是在装入内存时边装入边链接的,即在装入一个目标模块时,若发生一个外部模块调用事件,将引起装入程序去找出相应的外部目标模块,并将它装入内存。装入时动态链接方式有以下优点:

(1)便于修改和更新,由于各目标模块是分开存放,所以要修改或更新各目标模块是件非常容易的事。

(2)便于实现对目标模块的共享,在采用静态链接方式时,每个应用模块都必须含有其目标模块的拷贝,无法实现对目标模块的共享,但采用装入时动态链接方   式,OS 很容易将一个目标模块链接到几个应用模块上,实现多个应用程序对该模块的共享。

2.3 可以执行于运行时,由应用程序来执行(运行时动态链接)。

这种链接方式是将对某些模块的链接推迟到程序执行时才进行链接,即在执行过程中当发现一个被调用模块尚未装入内存时,立即由OS去找到该模块并将之装入内存,把他链接到调用者模块上。凡在执行过程中未被用到的目标模块,都不会被调入内存和被链接到装入模块上,这样不仅可加速程序的装入过程,而且可节省大量的内存空间。

为了理解链接,先看看目标文件,目标文件有三种形式:

(1) 可重定位目标文件 :包含二进制代码和数据,其形式可以在编译时与其他可重定位目标文件合并起来,创建一个可执行目标文件。

(2)可执行目标文件 :可以包含二进制代码和数据,其形式可以被直接加载到存储器并执行。

(3)共享目标文件 :一种特殊类型的可重定位目标文件,可以在加载或者运行时被动态地加载到存储器并链接。

编译器和汇编器生成可重定位目标文件(包括共享目标文件)。连接器生成可执行目标文件。不同系统之间,目标文件格式不相同,下一篇文章会专门讲ELF的格式


0 0