目录
前言
在多道程序环境下,要使程序运行,必须先为之创建进程。而创建进程的第一件事,便是将程序和数据装入内存。将一个用户源程序变为一个可在内存中执行的程序,通常都要经过以下几个步骤:
(1) 编译,由编译程序(Compiler)将用户源代码编译成 CPU 可执行的目标代码,产生了若干个目标模块(Object Module)(即若干程序段)。
(2) 链接,由链接程序 (Linker)将编译后形成的一组目标模块(程序段),以及它们所需要的库函数链接在一起,形成一个完整的装入模块(Load Module)。
(3) 装入,由装入程序(Loader)将装入模块装入内存。
以下的内容主要是一些概念,很大一部分来源于汤子瀛的《计算机操作系统》。
静态编译与动态编译
(1) 静态编译,就是编译器在编译可执行文件的时候,将可执行文件需要调用的对应静态库(.a或.lib)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行的时候不依赖于动态链接库。
(2) 动态编译的可执行文件需要附带一个动态链接库,在执行的时候,需要调用对应动态链接库的命令。所以其优点是缩小了执行文件本身的体积,另一方面是加快了编译速度。缺点是简单的程序,只用到了链接库的几条命令,也需要附带一个相对庞大的链接库。若计算机上没有安装相应的库,则用动态编译的可执行文件不能运行。
程序链接
源程序经过编译后,可得到一组目标模块,再利用链接程序将这组目标模块链接,形成装入模块。根据链接时间的不同,可把链接分成如下三种:
(1) 静态链接。在程序运行之前,先将各目标模块及它们所需的库函数,链接成一个完整的装配模块,以后不再拆开。把这种事先进行链接的方式称为静态链接方式。
(2) 装入时动态链接。这是指将用户源程序编译后所得到的一组目标模块,在装入内存时,采用边装入边链接的链接方式。
(3) 运行时动态链接。这是指对某些目标模块的链接,是在程序执行中需要该(目标)模块时,才对它进行的链接。
装入时动态链接
用户源程序经编译后所得的目标模块,是在装入内存时边装入边链接的,即在装入一个目标模块时,若发生一个外部模块调用事件,将引起装入程序去找出相应的外部目标模块,并将它装入内存,还要修改目标模块中的相对地址。装入时动态链接方式有以下优点:
(1) 便于修改和更新。对于经静态链接装配在一起的装入模块,如果要修改或更新其中的某个目标模块,则要求重新打开装入模块。这不仅是低效的,而且有时是不可能的。若采用动态链接方式,由于各目标模块是分开存放的,所以要修改或更新各目标模块是件非常容易的事。
(2) 便于实现对目标模块的共享。在采用静态链接方式时,每个应用模块都必须含有其目标模块的拷贝,无法实现对目标模块的共享。但采用装入时动态链接方式,OS则很容易将一个目标模块链接到几个应用模块上,实现多个应用程序对该模块的共享。
运行时动态链接
在许多情况下,应用程序在运行时,每次要运行的模块可能是不相同的。但由于事先无法知道本次要运行哪些模块,故只能是将所有可能要运行到的模块都全部装入内存,并在装入时全部链接在一起。显然这是低效的,因为往往会有些目标模块根本就不运行。比较典型的例子是作为错误处理用的目标模块,如果程序在整个运行过程中都不出现错误,则显然就不会用到该模块。运行时动态链接方式,是对上述在装入时链接方式的一种改进。这种链接方式是将对某些模块的链接推迟到程序执行时才进行链接,亦即,在执行过程中,当发现一个被调用模块尚未装入内存时,立即由 OS 去找到该模块并将之装入内存,把它链接到调用者模块上。凡在执行过程中未被用到的目标模块,都不会被调入内存和被链接到装入模块上,这样不仅可加快程序的装入过程,而且可节省大量的内存空间。
程序装入
在多道程序环境下,所得到的目标模块的起始地址通常是从 0 开始的,程序中的其它地址也都是相对于起始地址计算的。此时应采用可重定位装入方式,根据内存的当前情况,将装入模块装入到内存的适当位置。
静态地址重定位
静态地址重定位:即在程序装入对目标代码装入内存的过程中完成,是指在程序开始运行前,程序中指令和数据的各个地址均已完成重定位,即完成虚拟地址到内存地址映射。地址变换通常是在装入时一次完成的,以后不再改变。
优点:无需硬件支持。
缺点:(1) 程序重定位之后就不能在内存中移动了。
(2) 要求程序的存储空间是连续的,不能把程序放在若干个不连续的区域中。
动态地址重地位
动态地址重定位:不是在程序执行之前而是在程序执行过程中进行地址变换。更确切的说,是把这种地址转换推迟到程序真正要执行时才进行,即在每次访问内存单元前才将要访问的程序或数据地址变换成内存地址。动态重定位可使装配模块不加任何修改而装入内存。为使地址转换不影响指令的执行速度,这种方式需要一个重定位寄存器的支持,
优点:(1) 目标模块装入内存时无需任何修改,因而装入之后再搬迁也不会影响其正确执行,这对于存储器紧缩、解决碎片问题是极其有利的;
(2) 一个程序由若干个相对独立的目标模块组成时,每个目标模块各装入一个存储区域,这些存储区域可以不是顺序相邻的,只要各个模块有自己对应的定位寄存器就行。
缺点:需要硬件支持。
基本分页存储管理的地址变换
逻辑地址空间与物理地址空间
编译后,每个目标模块都从 0 号单元开始编址,称为该目标模块的逻辑地址(或相对地址)。当链接程序将各个模块链接成一个完整的可执行目标程序时,链接程序顺序依次按各个模块的地址构成统一的从 0 号单元开始编址的逻辑地址。不同进程可以有相同的逻辑地址,因为这些相同的逻辑地址可以映射到主存的不同位置。
物理地址是指内存中物理单元的集合,它是地址转换的最终地址,进程在运行时执行指令和访问数据,最后都要通过物理地址从内存中存取。当装入程序将可执行代码装入内存时,必须通过地址转换将逻辑地址转换成物理地址,这个过程称为地址重定位。
地址变换将逻辑地址转换为物理地址。通常在系统中设置一个页表寄存器(PTR Page-Table Register),存放页表在内存中起始地址 F 和页表长度 M。进程在未执行时,页表的起址和页表长度放在进程控制块(PCB)中,当进程被调度时,操作系统内核会把它们放在页表寄存器中。
逻辑地址到物理地址变换的过程:
(1) 计算页号 P 和页内偏移量 W。
(2) 比较页号 P 和页表长度 M,如果 P >= M,则会抛出越界异常。
(3) 页表中页号 P 对应的页表项地址 = 页表始址 + 页号 * 页表项长度,取出该页表项内容 b,即内存块号。
(4) 计算实际物理地址 = b * L + W 。
在分页存储管理(页式管理)系统中,只要确定了每个页面的大小,逻辑地址结构就确定了。页式管理中地址是一维的,即只要给出一个逻辑地址,系统就可以自动算出页号、页内偏移量两个部分,并不需要显示告系统这个逻辑地址中,页内偏移量占多少位。基本地址变换结构需要访问两次内存:第一次访问内存查找页表;第二次访问物理内存对应的内存单元。