程序是由若干个逻辑分段组成的,如可由代码分段、数据分段、栈段、堆段组成。不同的段是有不同的属性的,所以就用分段(Segmentation)的模式把这些段分离出来。
分段机制下,虚拟地址和物理地址是如何映射的?
分段机制下的虚拟地址由两局部组成,段抉择子和段内偏移量。
内存分段 - 寻址的形式
段抉择子就保留在段寄存器外面。段抉择子外面最重要的是段号,用作段表的索引。段表外面保留的是这个段的基地址、段的界线和特权等级等。
虚拟地址中的段内偏移量应该位于 0 和段界线之间,如果段内偏移量是非法的,就将段基地址加上段内偏移量失去物理内存地址。
在下面了,晓得了虚拟地址是通过段表与物理地址进行映射的,分段机制会把程序的虚拟地址分成 4 个段,每个段在段表中有一个项,在这一项找到段的基地址,再加上偏移量,于是就能找到物理内存中的地址,如下图:
图片
内存分段 - 虚拟地址与物理地址
如果要拜访段 3 中偏移量 500 的虚拟地址,咱们能够计算出物理地址为,段 3 基地址 7000 + 偏移量 500 = 7500。
分段的方法很好,解决了程序自身不须要关怀具体的物理内存地址的问题,但它也有一些不足之处:
第一个就是内存碎片的问题。
第二个就是内存替换的效率低的问题。
接下来,说说为什么会有这两个问题。
咱们先来看看,分段为什么会产生内存碎片的问题?
咱们来看看这样一个例子。假如有 1G 的物理内存,用户执行了多个程序,其中:
游戏占用了 512MB 内存
浏览器占用了 128MB 内存
音乐占用了 256 MB 内存。
这个时候,如果咱们敞开了浏览器,则闲暇内存还有 1024 – 512 – 256 = 256MB。
如果这个 256MB 不是间断的,被分成了两段 128 MB 内存,这就会导致没有空间再关上一个 200MB 的程序。
内存碎片的问题
这里的内存碎片的问题共有两处中央:
内部内存碎片,也就是产生了多个不间断的小物理内存,导致新的程序无奈被装载;
外部内存碎片,程序所有的内存都被装载到了物理内存,然而这个程序有局部的内存可能并不是很常应用,这也会导致内存的节约;
针对下面两种内存碎片的问题,解决的形式会有所不同。
解决内部内存碎片的问题就是内存替换。
能够把音乐程序占用的那 256MB 内存写到硬盘上,而后再从硬盘上读回来到内存里。不过再读回的时候,咱们不能装载回原来的地位,而是紧紧跟着那曾经被占用了的 512MB 内存前面。这样就能空缺出间断的 256MB 空间,于是新的 200MB 程序就能够装载进来。
这个内存替换空间,在 Linux 零碎里,也就是咱们常看到的 Swap 空间,这块空间是从硬盘划分进去的,用于内存与硬盘的空间替换。
再来看看,分段为什么会导致内存替换效率低的问题?
对于多过程的零碎来说,用分段的形式,内存碎片是很容易产生的,产生了内存碎片,那不得不从新 Swap 内存区域,这个过程会产生性能瓶颈。
因为硬盘的访问速度要比内存慢太多了,每一次内存替换,咱们都须要把一大段间断的内存数据写到硬盘上。
所以,如果内存替换的时候,替换的是一个占内存空间很大的程序,这样整个机器都会显得卡顿。
为了解决内存分段的内存碎片和内存替换效率低的问题,就呈现了内存分页。