内存分段

Sunday, October 27, 2019

分段

通过上文可知,地址空间中,堆和栈之间有一片空闲的空间并未被使用,这会造成极大的浪费,所以为了解决这个问题,操作系统引入了分段机制。

分段:泛化的基址/界限

我们知道,地址空间里主要存在3个逻辑不同的段:代码,堆,栈,我们可以在 MMU 中引入不止一对基址/界限寄存器,而是每个逻辑段引入一对,也就是3对,从而使得地址空间可以分段放置。

OS16.2

分段后的地址转换计算

前面我们有说过,基址会记录实际的物理内存位置,我们通过虚拟内存地址+寄存器保存的地址,即可得到实际的引用地址。但是在这里,因为引入了多个基址/界限寄存器分别给代码,堆,栈使用,转换地址的时候我们需要考虑偏移量问题。

比如在虚拟地址空间中,引用堆的地址4200 ,堆地址从4kb 开始。 我们再看上图,堆在34kb(假设代码占用了2kb) 开始,如果用4200加上基址(34kb),得到39016,这不是正确的地址,我们需要减掉偏移量,4200的偏移量实际是4200-4096,得到104,34kb+104=34920,这才是真正的物理地址。

引用哪个段

硬件在地址转换时使用段寄存器,如何知道段内的偏移量,以及地址引用了哪个段?

我们可以用一种显式的方式,在虚拟地址的开头两位标记不同的段,比如00是代码段,01是堆,剩下的12位用来标记段内偏移,比如下图标记了虚拟堆地址4200的偏移量。 OS16.3

栈怎么办

我们从第一张图可以看到,栈的增长方向是不同的,从28KB,增长回到26KB,虚拟地址从16KB 到14KB,所以地址转换要有所不同。

所以我们还需要知道段的增长方向(用1位来区分,比如1是自然增长,0是反向)

支持共享

有时候在地址空间之间共享某些内存段是有用的,比如代码共享。 为了支持共享,需要一些额外的硬件支持,比如保护位,位每个段增加几个位,标识程序是否能够读写该段,或者执行其中的代码。 OS16.4

操作系统支持

我们现在可以知道,利用了分段技术后,能大量节省了物理内存,但是这样改变后,操作系统该如何进行上下文切换,这个也就跟之前的一样,各个段的寄存器中的内容必须要保存和恢复。

第二个问题是管理物理内存的空闲空间,我们可以料想到的是,随着进程的使用,物理内存很快会有很多空闲空间的小洞,很难分配给新的段,或扩大已有的段。

OS16.5

一种解决方案是紧凑物理内存,重新去安排原有的段,比如先终止运行的进程,将数据复制到连续的内存区域,改变寄存器的值,但这样会占用大量的处理器时间。

另一种方法是利用空闲列表管理算法,尽量保留大的内存块用于分配,比如最优匹配,最坏匹配,首次匹配,伙伴算法等。

OS

空闲空间管理

抽象:地址空间