3.程序装载到内存(静态链接)

同样的电脑,同样的CPU,同一个程序在不同的操作系统上却不一样

在linux跑起来的系统在windows却跑不起来,这是为什么?

这设计到了编译的具体原理,前面讲到高级语言编译会编程汇编代码然后由汇编器翻译为机器码,但这只是笼统的说法

举个例子,有个main函数其中调用函数A,用objdump获取他们的汇编代码,会发现他们的地址都是从零开始,那么问题来了,都是0先执行谁呢?

事实上这两个都不是一个可执行文件,而是一个目标文件(对于编译型语言例如C语言来说,编译完后会生成目标文件),然后链接器把目标文件以及各种调用的函数库链接到一起才会得到一个可执行文件

事实上 C语言代码——汇编代码——机器码 这个过程有两部分

1.编译,汇编得到(目标文件),再链接,完成这三阶段我们得到一个可执行文件

2.通过装载器把可执行文件装载到内存上,CPU在从内存中读取指令和数据,这才是真正执行程序

回到目标文件和可执行文件,后者更前者长得差不多但是会长很多,

在linux操作系统中目标文件和可执行文件使用的是ELF(可执行与可链接文件格式)的文件格式,上述之所以会更长是因为在linux下,ELF不止包括汇编指令还有其他数据(诸如你定义的变量,函数等等,这些都会有相应的地址)

在ELF中有专门存储这些的东西称为符号表,相当于地址簿把名字与地址关联到一起

总结ELF文件:

1.代码段,保存程序代码和指令

2.数据段,保存你初始化的变量数据

3.重定位表,记录跳转地址,例如我们在链接发生前是不知道跳转到哪的,但我们可以通过重定位表定位跳转位置

4.符号表,保存里函数等名称和地址

工作顺序:

1.链接器扫描所有输入的目标文件,然后收集符号表信息,再根据重定位表吧所有不确定跳转地址的代码根据符号表的地址重新修正,最后合并对应的代码段,得到可执行代码

2.然后由装载器把这可执行代码装载到内存中,由CPU读取然后执行

回到最初的问题,两个系统不互通就是可执行代码的文件格式不同,他们各自的装载器不能解析对方的代码

总结:我们的程序不仅仅是吧代码一个个编译执行,而是分成不同的函数库最后通过静态链接,对于ELF文件实现静态链接,不只是需要程序执行的指令还包括了重定位表和符号表。

程序装载带来的问题:

事实上程序装载到内存中面临两个问题

1.可执行程序加载后占的内存应是连续的,因为程序计数器是顺序一条条执行下去的,这意味着指令要练在一起存储在内存

2.不能让程序自己决定占内存的加载位置,因为CPU会同时运行几个程序来提高效率,可能A的加载位置与B的位置相同从而使位置错乱(我们以为在那但实际上不在)

解决办法:在内存上找一段连续的空间,然后我们把这个空间与程序中的地址做一个映射(如程序中显示1,但内存中是10,程序中是2,内存是11),我们只需关注两个空间是否都是连续的以及映射的规则即可(映射是从一个头地址开始,到一个尾地址结束,若是一一把映射规则写出很繁琐,所以干脆只有一头一尾然后让两个空间都连续就行了)

指令中的称为虚拟内存地址,实际的内存硬件的地址称为物理内存地址,这种方法称为分段(这是我们在计算机中软硬件开发中常用的一种方法,加入一个间断层)

分段方法解决了程序本身不用关心加载位置的问题,但也带来新的问题

1.内存碎片,前面提到程序的执行是顺序,指令是连续的

如果一个1G的内存,用一个A程序占了100m,B占500m,C占100,D占剩下的

运行的时候删去B和D,理论上我们多了大约600m的内存但我们却运行不了有个600m的程序,为什么?

因为600是不连续的,500m在前面,100m在后面

解决办法:

程序员想到内存交换的办法,把500和100之间的内存写到硬盘上,这样500和100就接起来,最后从硬盘中把中间的读回来内存中。

【虚拟内存+分段+内存交换解决了计算机同时装载多个程序的问题,但是没解决速度问题,硬盘的访问速度比内存慢很多很多,且每次内存交换都相当于把一大段内存数据写到硬盘上,因此每次内存交换都会让机器很卡顿】

现在解决速度问题

既然问题是内存碎片和内存交换空间太大,那就把他变小(内存分页)

相对于分段这样一大段一大段分配连续的空间,分页是把整个物理内存空间切成一段段固定尺寸的大小,对应的虚拟内存空间也切成一段一段,从虚拟内存到实际内存的映射是按照一页一页来的,

由于空间划分完了,因此并没有内存碎片,释放出来的只是一个个页,然后一次写入磁盘的只有少数甚至一个页(相当于把内存碎片变成了一个个固定的页数,然后每次处理一点,相当于因为分类而更高效)

更进一步,分页的方式是我们装载程序时不需要一次性把程序全装载进内存,而是程序运行时需要对应的页的数据,我们再加载。

PS:这样的好处是什么呢,我们可以更高效的运转并执行,早在上个世纪比尔盖茨说640K足够任何人使用,虽然预测失败了,但在他那个年代并非没道理,640K可以分成很多页,而程序必须要执行的不过是一页罢了,形成动态滚动般的效果

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值