计算机内存使用方式

早期计算机内存使用方式

早期的计算机内存使用根本就没有所谓的抽象(考虑如何使用),比如下面这条汇编指令:

 MOVE REGISTER1,1000

这条汇编代码的作用为,将地址为1000的物理内存上的内容移到REHISTER1中,那是的物理内存给人的映像就为0到一个上限的集合,每个地址上能存一定数目的二进制数,一般都为八位。

以冯诺依曼的计算机设计思路为,CUP现在内存中取指(这里的,代表指令类型,比如接下来是要做加法还是减法,左移还是右移,构架不同的CUP所用的指令集是不同的,所以就有了X86,X64,ROM构架等等),再取到指令后在完成相应的操作,完成之后在将数据写回到内存。

而在这种情况下如果要运行多个程序是几乎不可能的,很好理解(不理解建议看看计算机原理)因为一个程序A如果在内存地址2000的位置处写了一个"l love you" 准备发给女朋友,这时候程序B刚好也要在内存种写数据了,它在2000位置写了一个"I hate you",而A程序并不知道就把B程序写的内容发出去。这样一来就会出现问题,如果是这个地址是指令的话,更严重,这两个程序就会直接崩溃掉。

虽然在早期也提出了些解决方法(保护键,重定位等等)简单说一下,接着上面的A和B说,因为A和B进程都想用2000这个位置,从而导致了内存错误,那么在运行的过程中对于加载到内存的程序每一个程序设置一个基址,程序就从基址开始存放,在寻址的时候原本的地址再加上基址就可以得到真实的物理地址,但是呢每条指令都需要进行加法运算,效率很低,还存在地址越界的情况。

本文中就不多做讨论了。因为现在除了一些小的嵌入式设备(如:洗衣机,电饭锅),或者航天设备外几乎都用内存映射的方式。这些嵌入式设备的代码事先都是确定好了的,各自运行各自,不会跑到别人的地址上去。

地址空间,虚拟内存,交换空间

程序的地址空间是指这个程序可以使用的物理内存大小,比如说8位的程序那么它的最多可用内存为2的8次方,16位程序2的16次方(64K),32位(4G),64位(16777216 TB)。也就是说程序可以任意访问自己内存位置的任意值。而现在计算机的内存如果说个人电脑一般都为8G,大一点的16G,而那么大的空间无法满足文章的后面会说明解决方法。

虚拟内存和交换空间,其实两者意思都差不多,一个是再window下的叫法一个是linux的叫法。基本思想都差不多:程序拥有自己的整个寻址空间,但是这些空间被分为很多块,每一块称作页面,每一页有连续的直至范围,这些页的部分被映射到内存中,在程序运行的时候如果内存中有,那么直接运行,如果没有,那么就由操作系统将缺失的部分从磁盘中装入物理内存再重新运行。这样还有一个好处,比如一些共享库,或者一个程序重复运行,它们的代码段可以共享。这样以来,虽然程序的地址空间很大,但是无关紧要,通过分页方法仍然可以运行。

分页

在这里插入图片描述
如图所示16位程序的64K地址空间:程序的虚拟地址空间是64K,但是物理内存只有32K,通过将虚拟地址空间分页,物理内存也进行分页,为了好区别一个称为页面,一个称为页框。这两个大小都是一致的。程序地址空间的叉叉是代表未映射的地方。程序图上的数字代表用了物理内存的那一块,这些数字虽然在图上标出,但实际上存在一个叫页表数据结构中。
MMU 是将虚拟地址映射为物理地址的一个硬件电路。
虽然进行了映射,但是根本上程序还是比内存大呀,解决方法如下:当MMU
发现一个页面未映射,例如8这个页面。这时MMU会发出一个缺页中断,这时操作系统就找一个很少使用的页框,并把它内容写回磁盘(腾空间)。之后再把8这个页面调入内存,程序继续执行。例如: 如果操作系统准备决定放弃页框1,操作如下,1,标记虚拟页面1的表项为未映射,使以后对虚拟内存地址4K~8K的内存都为未映射,产生缺页中断。之后将虚拟页面8对应的页框改为1,此时虚拟页面8对应真实的物理内存的框号为1。这时才回到开始原先页面中断处继续执行。

分页优点以及问题

由于将程序分成了很多页,很好的利用了内存资源,解决了8086之前存在的内存碎片,内存整理十分浪费时间的问题。
但是 世界上没有免费的午餐,分页也存在着自己的问题:
1)程序的虚拟地址空间很大的话,每个进程的页表项是十分的多的。假设32位程序的页面为4K(如果页面选的太大,可能存在着了浪费内存的问题,如果太小页表过多),32位程序地址空间为4GB,那么页表项也就十分的庞大,超过100万页。每个进程有100万页的页表项那么内存根本容纳不下。
2)虚拟地址到物理地址的映射是不是够快,因为每次访问内存都要经过虚拟内存映射的话,那么势必CPU每次都会多执行几条指令,这样一来运行的速度就会大大的折扣。
由于分页每次都要更次的访问内存,而访问内存的速度相比CPU的速度慢了很多,以至于早期很少使用分页机制。

TLB相联存储器

通过研究发现,程序的地址访问存在局部性。如图所示:
在这里插入图片描述
在这里插入图片描述
大多数程序总是对少量的页面进行多次的访问,因此只有很少的页表项被反复的读取,而其他页面很少被访问。

而TLB 为一种小型的存储器,将内存中经常需要用到的页表项存入其中,这时如果访问CPU需要要访问一个地址,这个地址在TLB中的话那么就通过TLB找到值(TLB速度非常快)然后直接访问物理内存,而不需要再到内存中寻找。如图所示:在这里插入图片描述
如果cpu访问的内存区域不在TLB中(称TLB未命中),那么就会进行正常的页表查询,之后在原有的TLB中淘汰一个页表项,然后将内存中的这块页表存入到TLB中(其实把可以这么理解:所有的页表信息其实都在内存中,TLB就是页表的一个高速缓冲区)。
这样页表导致访问内存速度慢的问题就得到了解决。

多级页表

上述的TLB技术一定程度上解决了内存访问慢的问题,但是如何处理页表项过多的情况仍然是一个问题。
方案一: 只存用到的项,虽然说这样可以大幅度减少内存的使用量,但是这样的方案会产生页面的不连续性。如果页面不连续了,就算使用二分搜索,每次肯能要是搜索10次左右,每条指令要多十多次的搜索,这样计算机的性能就会下降到十分之一,这样就变得不可接受。
在这里插入图片描述
而页表如果连续的话,就像数组,指针直接加上一个数值,那么就直接可以跳到相应的位置。所以说页表必须连续存储

这里就引出方案二多级页表:
在这里插入图片描述
32位的虚拟地址被划分位10位的PT1区,10位的PT2域和12位的偏移量,及一个顶级页表代表4M的空间,利用图书索引的思想。因为整个程序空间并不是都被用到,所以针对上图未被用到的23456顶级页表项可以不映射它的二级页表项。这样一来大量的二级域不用被映射,从而节约了内存。当然了上述的页表还可以改为更多级的,比如三级,四级。这样虽然可以更加减少内存的使用,但是没多一级,就会多一次访问时间,那么效率就会更加低下。

方案三倒置页表实际内存中每一个页框有一个页表项,而不是每一个虚拟页面有一个页表。

分段

上述的虚拟内存的内容大部分讨论都是一维的,及虚拟地址从0开始,一直到最大,但是0的位置用了再用1,一直类推这样会导致很多问题。比如一个编译器在编译过程中的确会建立很多的表,可能如下:
1)源程序正文
2)符号表,包含变量的名字和属性
3)编译器内部过程使用的堆栈等
当然一些段在程序编译好之后大小就不变了,但是向堆区和栈区空间大小可能就不确定。如果直接使用一维存储器,那么可以将每一个所需要的位置分一个块,如果一个程序有非常多的变量自己的块不够用,而别的块又有空闲,如果要借别的块的空间就会显得很麻烦。如图所示一维地址空间,表可能方式碰撞。
在这里插入图片描述
如图所示:源程序正文段和符号表碰在了一起。
这样一来在一维的指针空间中程序员还需要想每一段有多大,不能越界等等的问题,显得十分笨拙。
这里就提出了分段式内存:
在这里插入图片描述
将整个程序分成若干个段,每个段都构成一个独立的地址空间,它们可以独立的增长或者减小而又不会影响其他的段。而且在不同的程序中通过分段,如果有相同的代码段,共享库的话通过共用段也可以提升内存的利用率。通过分段的思想可以解决一维内存分块大小冲突的问题,分段信息存放在进程段表()中,PCB的中的一个指针指向它。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值