在多道程序环境下,要使程序运行,必须先为之创建进程。而创建进程的第一件事,便是将程序和数据装入内存。将一个用户源程序变为一个可在内存中执行的程序,通常都要经过以下几个步骤:
(1) 编译,由编译程序(Compiler)将用户源代码编译成 CPU 可执行的目标代码,产生了若干个目标模块(Object Module)(即若干程序段)。
(2) 链接,由链接程序 (Linker)将编译后形成的一组目标模块(程序段),以及它们所需要的库函数链接在一起,形成一个完整的装入模块(Load Module)。
(3) 装入,由装入程序(Loader)将装入模块装入内存。
以下的内容主要是一些概念,很大一部分来源于汤子瀛的《计算机操作系统》。
(1) 静态编译,就是编译器在编译可执行文件的时候,将可执行文件需要调用的对应(.a或.lib)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行的时候不依赖于动态链接库。
(2) 动态编译的可执行文件需要附带一个动态链接库,在执行的时候,需要调用对应动态链接库的命令。所以其优点是缩小了执行文件本身的体积,另一方面是加快了编译速度。缺点是简单的程序,只用到了链接库的几条命令,也需要附带一个相对庞大的链接库。若计算机上没有安装相应的库,则用动态编译的可执行文件不能运行。
源程序经过编译后,可得到一组目标模块,再利用链接程序将这组目标模块链接,形成装入模块。根据链接时间的不同,可把链接分成如下三种:
(1) 静态链接。在程序运行之前,先将各目标模块及它们所需的库函数,链接成一个完整的装配模块,以后不再拆开。把这种事先进行链接的方式称为静态链接方式。
(2) 装入时动态链接。这是指将用户源程序编译后所得到的一组目标模块,在装入内存时,采用边装入边链接的链接方式。
(3) 运行时动态链接。这是指对某些目标模块的链接,是在程序执行中需要该(目标)模块时,才对它进行的链接。
(1) 便于修改和更新。对于经静态链接装配在一起的装入模块,如果要修改或更新其中的某个目标模块,则要求重新打开装入模块。这不仅是低效的,而且有时是不可能的。若采用动态链接方式,由于各目标模块是分开存放的,所以要修改或更新各目标模块是件非常容易的事。
(2) 便于实现对目标模块的共享。在采用静态链接方式时,每个应用模块都必须含有其目标模块的拷贝,无法实现对目标模块的共享。但采用装入时动态链接方式,OS则很容易将一个目标模块链接到几个应用模块上,实现多个应用程序对该模块的共享。
在许多情况下,应用程序在运行时,每次要运行的模块可能是不相同的。但由于事先无法知道本次要运行哪些模块,故只能是将所有可能要运行到的模块都全部装入内存,并在装入时全部链接在一起。显然这是低效的,因为往往会有些目标模块根本就不运行。比较典型的例子是作为错误处理用的目标模块,如果程序在整个运行过程中都不出现错误,则显然就不会用到该模块。运行时动态链接方式,是对上述在装入时链接方式的一种改进。这种链接方式是将对某些模块的链接推迟到程序执行时才进行链接,亦即,在执行过程中,当发现一个被调用模块尚未装入内存时,立即由 OS 去找到该模块并将之装入内存,把它链接到调用者模块上。凡在执行过程中未被用到的目标模块,都不会被调入内存和被链接到装入模块上,这样不仅可加快程序的装入过程,而且可节省大量的内存空间。
优点:无需硬件支持。
缺点:(1) 程序重定位之后就不能在内存中移动了。
(2) 要求程序的存储空间是连续的,不能把程序放在若干个不连续的区域中。
优点:(1) 目标模块装入内存时无需任何修改,因而装入之后再搬迁也不会影响其正确执行,这对于存储器紧缩、解决碎片问题是极其有利的。
(2) 一个程序由若干个相对独立的目标模块组成时,每个目标模块各装入一个存储区域,这些存储区域可以不是顺序相邻的,只要各个模块有自己对应的定位寄存器就行。
缺点:需要硬件支持。