程序装入链接
程序装入
引言
首先明确下从.c程序到跑在计算机中都经历了什么过程?
- 首先将.c程序编译
- 编译以后由于使用了各个模块,需要进行链接操作
- 链接完成以后,生成了exe文件,需要将其装入内存中,此即装入
- 我们双击一个exe文件,其实就是触发了装入
- 装入以后就交给操作系统来完成(包括进程创建等过程)
- 至此我们就知道了从.c到跑在计算机中的程序都经历了哪些过程
后面讲解虚拟内存的时候需要用到程序装入链接的知识,所以这里介绍一下。
- 绝对装入
- 可重定位装入
- 动态运行时装入
首先将例子拿出来,有两个文件,第一个为main.c第二个为f.c,main.c中的main函数调用了f函数,如下所示:
main.c
void main(){
f();
}
f.c
int c=0;
void f(){
c=c+1;
}
最后的可执行程序可以理解成如下:
1.绝对装入
所谓绝对装入,即将各个指令的地址都确定下来,如下所示:
左图中的CALL f被替换成CALL [102]即将函数f的地址确定下来,同理变量c的地址也确定下来。
思考:这种装入方式有何优缺点?
- 不用额外的运算,十分简单高效
- 但是:一旦装入以后便不可移动,如果移动也需要将程序中比如CALL [102]中的数字改掉
- 将上述数字改掉,其实相当于重新编译一遍程序了,效率会很低,这就是最主要的缺点
2.可重定位装入
什么是可重定位装入呢?看下图:
上图和绝对装入的图区别不大,但是要注意到:左图的左边是标了数字的,即确定了各个指令的相对地址,如果将上述装入到100开始的连续内存空间中,那么直接将上述序号+100即可。
所以程序在内存中需要换位置的话,很方便
但是问题来了:
- 虽然我们对绝对装入做了改进,形成了可重定位装入的方法,但是其仍然有其缺点
- 的确是可以很方便的重新定位了,但指令中的[102]仍然需要重新写,和绝对装入的缺点类似
我们的思路是:能不能让程序中类似于[102]的数据保持完全不变,如果可以的话,上述缺点都克服了。
3.运行时装入
能想到上述改进思路以后,就可以进行设计了,我们的目的是实现:
这里的[2]真的是[2]嘛?
- 当然不是,这里写的的确是[2]
- 但是当CPU真正去获得[2]的内容时,需要经过转换
- 转换工作交给特定的硬件去完成:MMU
程序链接
链接也分为三种:
- 静态链接
- 装入时动态链接
- 运行时动态链接
首先理解下什么是链接:
- 还是看上面给出的程序
- 给出一个main.c再给出一个f.c
- main里调用了f函数,但是函数f不在main.c里面,所以需要将他们连接在一起
- 这个连接的过程就叫链接,笔者觉得不如直接叫连接容易理解
1.静态链接
将左边的两块程序链接成了一块程序,其中c为数据内容,所以提到了最前面。
如何理解“静态”两字?
- 静态是相对于动态来讲的
- 那么何为动态呢?需要看下面,所以需要将下面的动态链接看完才容易理解这里的静态链接
- 要生成exe文件,需要经过编译、链接,这里的静态链接即将所有文件都直接打包成一个,直接装入内存就完全OK了,可以结合下面的动态链接一起理解
注意到静态链接的一个缺点:将所有模块都融合一起了,exe文件体积必然会很大
2.装入时动态链接
此为动态链接的一种,即编译完成以后不链接,防止产生的exe文件太大,等装入的时候再链接
上述的静态链接是指:将所有模块一起打包全部搞到一个exe文件中,直接装入内存运行就OK了,这里的动态链接则不同:
是指先生成main.c的exe,等装入时,再将f对应的文件链接,并装入内存,这种方案有效解决了exe文件过大的问题:
上述方案虽然能降低exe文件所占的体积,但是最后装入内存的东西仍然很多,那有没有节省内存的方案呢?
3.运行时动态链接
此方案再次做了改进,即我现生成main.c的exe文件,将其自己直接装入内存,运行过程中发现其中需要调用f,此时再将f所在的模块装入,那么在未调用f之前,其内存占用是比装入时动态链接低的:
链接时机是:运行过程中用到再装入。
分析静态链接和动态链接的优劣?
- 首先:静态链接产生的可执行文件较大,动态链接产生的可执行文件较小
- 其次:动态链接的一个优点:模块版本升级容易,比如f函数重写,那么替换掉f生成的动态链接库即可,不用重新编译main,但是静态链接需要重新编译
- 虽然动态链接有上述优点:但是如果f写的不兼容怎么办?main执行过程崩溃掉了,但是如果使用静态链接并重新编译一遍便可在一定程度上缓解此类事件发生,因为最起码通过了编译器的语法检验
- 静态链接启动时间长,动态链接启动时间短
- 静态链接一次装入即可,如果运行时动态链接,可能影响程序运行效率
如果觉得写的不错,对你有帮助,点个赞鼓励一下叭~