一.从写程序到程序运行
程序员通过编辑器编辑源代码文件,通过编译后会生成与源代码文件对应的目标模块文件,并且这些
模块中包含若干指令,这些指令中包含逻辑地址。
编译:
由编译程序将用户源代码编译成若干个目标模块(编译就是把高级语言翻译为机器语言)
接下来就是链接,即把目标模块组合起来形成相应的装入模块,其含有完整的逻辑地址,即可执行模块。
链接:
由链接程序将编译后形成的一组目标模块,以及所需库函数链接在一起,形成一个完整的装入模块
接下来将模块装入内存中,并且将其逻辑地址转换为物理地址。
装入(装载):
由装入程序将装入模块装入内存运行。
二.链接
上面提到过,链接就是由链接程序将编译后形成的一组目标模块,以及所需库函数链接在一起,形成一个完整的装入模块,现在讲一下链接的几种方式。
1.静态链接:在程序运行之前,先将各目标模块及它们所需的库函数连接成一个完整的可执行文件(装入模块),之后不再拆开。
在形成装入模块后,就形成了装入模块的完整的逻辑地址。
在形成装入模块后,就形成了装入模块的完整的逻辑地址。
2.装入时动态链接:将各目标模块装入内存时,边装入边链接的链接方式。
使用这一方法,目标模块的完整逻辑地址是一边装入一边形成的。
3.运行时动态链接:在程序执行中需要该目标模块时,才对它进行链接。如下图,若程序不需要使用b()函数,就不将其对应的目标模块装入即可。其优点是便于修改和更新,便于实现对目标模块的共享。
三.如何将逻辑地址转为物理地址
上面提到装入时需要将逻辑地址转换为物理地址,现在来讨论一下装入的方法。
1.绝对装入
绝对装入:在编译时,如果知道程序将放到内存中的哪个位置,编译程序将产生绝对地址的目标代码。装入程序按照装入模块中的地址,将程序和数据装入内存。(也就是将编译程序中各变量的地址修改为正确的绝对地址)
例如:如果直到装入模块要从地址为100的地方开始存放
装入内存,从100开始装入:
缺点:灵活性很差,只适用于单道程序环境。
2.可重定位装入(静态重定位)
静态重定位:又称可重定位装入。编译、链接后的装入模块的地址都是从0开始的,指令中使用的地址、数据存放的地址都是相对于起始地址而言的逻辑地址。可根据内存的当前情况,将装入模块装入到内存的适当位置。装入时对地址进行“重定位”,将逻辑地址变换为物理地址(地址变换是在装入时一次完成的)。
与绝对装入的区别在于,静态重定位是在程序装入内存时,再进行地址转换,将逻辑地址转为物理地址。
静态重定位的特点:
•在一个作业装入内存时,必须分配其要求的全部内存空间,如果没有足够的内存,就不能装入该作业。
•作业一旦进入内存后,在运行期间就不能再移动,也不能再申请内存空间。
•一般用于早期的多道批处理操作系统。
3.动态运行时装入(动态重定位)
动态重定位:又称动态运行时装入。编译、链接后的装入模块的地址都是从0开始的。装入程序把装入模块装入内存后,并不会立即把逻辑地址转换为物理地址,而是把地址转换推迟到程序真正要执行时才进行。因此装入内存后所有的地址依然是逻辑地址。这种方式需要一个重定位寄存器的支持。
重定位寄存器:
存放了装入模块存放的起始位置,例如下图,起始位置为100。
当CPU访问指令0时,会将其中的逻辑地址79,与重定位寄存器的起始位置相加,得到其访问的地址。
与静态重定位的区别在于,动态重定位装入时依旧保持使用逻辑地址。
动态重定位的特点:
•采用动态重定位时允许程序在内存中发生移动。
•可将程序分配到不连续的存储区中;
•在程序运行前只需装入它的部分代码即可投入运行,然后在程序运行期间,根据需要动态申请分配内存;
•便于程序段的共享,可以向用户提供一个比存储空间大得多的地址空间。