(1)非连续内存分配的原因
-连续内存分配(上一节内容)的缺点
->分配给一个程序的物理内存是连续的
->内存利用率低
->有外碎片/内碎片问题
-非连续内存分配的优点
->分配给一个程序的物理内存是非连续的
->更好的内存利用和管理
->允许共享代码和数据(共享库等)
->支持动态加载和动态链接
-非连续内存分配的缺点
->如何建立虚拟地址和物理地址之间的转换
—>软件方案(开销大)
—>硬件方案
-两种硬件方案
->分段Segmentation
->分页Paging
-程序的分段地址空间
逻辑地址空间是连续的,物理地址是离散的。
-分段寻址的方案
->以上开销大,如何用硬件实现
(1)段访问机制:一个段指一个“内存块”,是一个逻辑地址空间。
程序根据段访问机制访问内存地址需要一个二维的二元组(s段号,addr端内偏移)
划分物理内存至固定大小的帧
->大小是2的幂,e.g.512,4096,8192
-划分逻辑地址空间至相同大小的页
->大小是2的幂,e.g.512,4096,8192
-建立方案:转换逻辑地址为物理地址(pages to frames)
->页表Page Table
->MMU/TLB(快表)
(1)页帧:物理内存被分割为大小相等的帧(物理地址部分)
-一个内存的物理地址是一个二元组(f,o),f:帧号(它是F位的,因此意味着一共2F个帧);o:帧内偏移(它是S位的,因此意味着每帧有2S字节);物理地址=2^S x f + o。
(2)通过一个实例来加深对这个概念的理解,
首先有一个物理内存地址(3,6),帧号是3,它是7位的,说明一共能有2^7个帧,这个帧是其中的第3个;
帧内偏移是6,他是9位的,说明一个帧里可以有2^9个字节,当前地址是在这个帧里的第6个字节;
因此,它的物理地址是3 * 2^9 + 6。
通过这个例子,可以发现 页帧号 的作用就是通过一个二元组能够找到一个物理地址。
(1)页:一个程序的逻辑地址空间被划分为大小相等的页(逻辑地址部分)
-(逻辑地址的)页内偏移量=(物理地址的)帧内偏移量
-(逻辑地址的)页号大小可能不等于(物理地址的)帧号大小
通过这个例子,可以发现 页号 的作用就是通过一个二元组能够找到一个逻辑地址。页号和帧号不是相等的。
(2)页寻址机制的实现
页表实际上就是一个大的数组/hash表。它的index是 页号,对应的value是 页帧号,首先根据逻辑地址计算得到一个 页号,也就是index,再在页表中找到对应的 页帧号,最后根据 页帧号 计算得到物理地址;由于他们的页/帧内偏移相等,所以页表不需要保存这个数据。通过这种方式能够根据逻辑地址找到对应的物理地址。
除此之外,还有一些flags标志位,见下一节。
(2)如下图,看下面这个例子。逻辑地址空间是16位64kB,物理地址空间是15位32kB,并不是对等的,但是一页和一帧的大小是一样的,都是10位1kB。
->逻辑地址(4,0),页号4对应的二进制是100,它的位置对应着flags。根据上图,可知它的dirty bit是1,resident bit是0,clock/reference是0;因此可以知道逻辑地址(4,0)在物理地址中实际是不存在的。如果CPU访问这个逻辑地址会抛出一个内存访问异常;
->逻辑地址(3,1034),页号3对应的二进制是011,它的位置(也就是页号的位置)对应着flags。根据上图,可知它的dirty bit是0,resident bit是1,clock/reference是1;因此可以知道逻辑地址(3,1034)在物理地址中存在。再复习一下,页表里存放的是什么。因为由于逻辑地址的页内偏移和物理地址的帧内偏移是一样的,所以页表不需要保存偏移。根据页表,页号3对应的页帧号是4,再加上它们的偏移量相等,所以逻辑地址(3,1034)对应的物理地址是(4,1023)。
这个页表是由操作系统维护。
(3)上面讲的分页机制的性能问题(缺点)
空间代价和时间代价。
->访问一个内存单元需要2次内存访问:一次获取页表项;一次是访问数据。
->页表可能会非常大(页表的长度等于2^页号位数)
举例,64位机器,如果一页是1024KB,那么页表是多大?
假如页号是n位的,那么页表的长度等于2^ n,一页是1024KB,所以页内偏移是10位,一个逻辑地址的长度等于计算机位数,也就是64位,因此剩下的54位是留给页号的;因此页表的长度是2^54,明显CPU装不下。
一个程序一个页表,n个程序n个页表,就更大了。
CPU装不下,只能装在内存里;如果这样,需要访问2次内存,一次访问页表,一次访问程序。
(4)解决办法
->缓存caching
->间接访问indirecion
本节介绍如何优化页表的时间开销问题,解决办法是 缓存。
TLB实际上是CPU的MMU内存管理单元保存的一段缓存,这段缓存保存的内容是 页表 的一部分,是经常访问到的那部分页表,其余不常用的页表内容保存在内存中。
TLB未命中,也叫TLB miss,这种情况比较少见,因为一页很大,32位系统一页是4K,如果采用局部性原理,那么访问4k次才会遇到一次TLB miss。
本节介绍如何优化页表的空间开销问题,解决方法是 多级页表。
虽然增加了内存访问次数和开销,但是节省了保存页表的空间(时间换空间,然后在通过TLB来减少时间消耗)。
(1)二级页表
逻辑地址中,页号部分分成了2部分,p1和p2。
p1存放着二级页表的起始地址,p2的作用就是之前的p。
p1找二级页表,p2找页,o找地址。
这里体现了二级页表的另一个好处,就是p1对应的位置是flags,假如说resident bit是0,那么整个二级页表都不用在内存中保存,这个是一级页表无法实现的!
反向页表:
页表来表示物理地址(页帧)号,而不是之前的逻辑地址(页号),能够减少页表尺寸,但是给映射关系的建立带来点困难。
(1)对于大地址空间,前向映射页表变得繁琐(例如64位系统采用5级页表)。
(2)逻辑地址空间增长速度快于物理地址空间,所以反向页表,也就是index是物理地址,value是逻辑地址,它的大小会小于传统页表。
residence bit:此帧是否被占用;
occupier:对应的页号p;
protection bit:保护位;
(2)举一个例子
物理内存大小:4096 * 4096 KB = 16 MB
页面大小:4064 bytes = 4 KB
页帧数: 4K
页寄存器使用的空间(假设是8 bytes的register):8 * 4096 = 32 KB
页寄存器的额外开销:32 KB / 16 MB = 0.2%
虚拟内存的大小:任意
可以看出内存开销很小。
(3)页寄存器的优点
转换表的大小相对于物理内存来说很小;
转换表的大小跟逻辑地址空间的大小无关。
(4)页寄存器的缺点
需要的信息对调了,如何根据帧号找到页号呢;
需要在反向页表里去找想要的页号。
如果帧数较少,页寄存器可以被放置在关联内存中;
在关联内存中查找逻辑页号,成功了,帧号就被提取出来;失败了,页错误异常page fault。
限制这种方案的因素包括,大量的关联内存非常昂贵(难以在单个时钟周期内完成;耗电)。
上述方法可能导致一个key出现2个以上的value对应。
这种方式仍然需要把反向页表放在内存中,做hash计算的时候还需要在内存中取数,仍然需要多次访问内存。
参考资料
https://www.bilibili.com/video/av6538245?p=19