虚拟内存- MMU 和 TLB - 多级页表

虚拟内存- MMU 和 TLB - 多级页表

  • 在计算机中,我们直接操作物理地址,是一种效率底下的的方式,而虚拟内存的出现,则大大加速了程序员的开发能力。(虽然这降低了性能,不过是 Trade off 罢了)。
  • 在 CPU 中内存管理单元( Memory Management Unit , MMU ),负责虚拟地址到物理地址的转换。
  • 随着计算机的发展,人们发现对于这种方式,不是很高效,于是便引入了 转址旁路缓存 ( Translation Lookaside Buffer , TLB )

分段与分页机制

  • MMU将虚拟地址翻译为物理地址的主要机制有两种。
    • 分段机制
    • 分页机制
分段机制
  • 分段机制下,操作系统以 “段” ( 一段连续的物理内存) 的形式来管理/分配内存。
  • 一个应用程序的虚拟地址空间由若干个大小不同的段组成,有代码段,数据段。
  • 当 CPU 访问虚拟地址空间中的某一个段时,MMU 会通过查询段表得到该段的物理内存区域。
  • 一个虚拟地址由两部分组成
    • 第一个段表示段号,标识该虚拟地址属于整个虚拟地址空间的哪一段
    • 第二个表示段内地址,相当于段内偏移。
    • 这个过程有点像 CS : IP 寄存器找到指令的过程。CS 负责存储段地址,IP负责存储偏移地址。
  • MMU 翻译步骤
      1. MMU 通过段表基址寄存器找到段表的位置。结合待翻译虚拟地址中的段号,可以在段表中定位到对应段的信息。
      1. 然后取出该段的起始地址( 物理地址 ),加上待翻译虚拟地址中的段内地址( 偏移量 ),就能够得到最终的物理地址。
  • 分段机制的好处 与 坏处
    • 好处: 在这种的机制下,物理地址和虚拟地址都被划分成了多个离散的区域。操作系统可以实现物理内存资源的离线分配。
    • 坏处: 试想一下,我一个 4GB 的物理内存,被分成了4段,0 - 1 GB, 1 - 3 GB, 3 - 3.5 GB, 3.5 - 4GB,当我的 1 - 3 GB 的内存 和 3.5 - 4GB 空闲时,我想分配 1.5 GB 给应用程序,由于他们是不连续的,会分配失败!!!。
  那么如何解决分段机制所带来的内存使用不够充分的坏处呢?
  分页机制便引入。
分页机制
  • 分页机制的思想是将应用程序的虚拟地址空间划分为连续的.等长的物理页。同时物理内存也被划分为连续的,等长的物理页。
  • 虚拟页和物理页的页长固定且相等,从而使得操作系统能够很方便的为每个应用程序构造页表。-------虚拟页到物理页的映射关系表。
  • 逻辑上,该机制下的虚拟地址也由两个部分组成,
    • 第一部分标识虚拟地址的虚拟页号
    • 第二部分标识虚拟地址的页内偏移量
  • 在具体的地址翻译过程中,MMU 首先解析得到虚拟地址中的虚拟页号,再通过该虚拟页号去该应用程序的页表( 页表起始地址存放在页表基地址寄存器中 ) 找到对应条目,然后取出条目中存储的物理页号,最后用该物理页号对应的物理页起始地址加上虚拟地址中的页内偏移量得到最终的物理地址。
  • 在分页机制下,有效解决了分段机制下的内存碎片问题。
 我亲爱的同学啊,试想一下,对于 64 位的虚拟地址空间,
 这个页表有多大呢,假设页的大小是 4KB,页表每一项大小是
 8字节,那么一张页表的大小就是 2的 64 次方 * 8 / 4KB ,大概是 33554432 GB !!
 
 那么怎么优化呢?多级页表也就顺势而为了!
 ( Trade off )
  • 为了压缩 页表大小,操作系统引入了多级页表。用来满足虚拟内存在空间 高效性的要求。
  • 在使用多级页表时,一个虚拟地址中依然包括虚拟页号和业内偏移量,其中虚拟页号将被进一步地划分为 K 个部分,
  • 虚拟页号对应于该虚拟地址在 第 i 级的索引。当任意一级页表中的某个条目为空时,该条目对应的下一级页表不需要存在,依次类推。接下来的页表也不需要存在。
  • 此刻读者可以把他理解为一种多叉树的结构( B+ 树 )。
AArch 64 环境下的 4 级页表
  • 虚拟地址低48位参与地址翻译,页表级数为4级,虚拟页大小为 4 KB。
  • 在此设置下,物理内存被划分为连续的 4KB大小的物理页,一个虚拟页可以映射对应一个物理页。因为页的大小是 4KB,所以虚拟地址的 低12位对应的是业内偏移量。整个页表的起始地址 ( 物理地址 ) 存储在一个特殊的寄存器中。
  • 第0级( 顶级 )页表有且仅有一个页表页,页表基地址寄存器存储的就是该页的物理地址。其余每一级页表拥有若干个离散的页表页,每一个页表页也占用物理内存中的一个物理页。
  • 一个 64 环境下的虚拟地址在逻辑上被划分为如下几个部分
位段作用
63 – 48 位全为 0 / 1,硬件要求,
47 – 39 位这 9 位作为虚拟地址在第 0 级页表的索引
38 – 30 位这 9 位作为该虚拟地址在第 1 级页表的索引
29 – 21 位----------------------2 级--------
20 – 12 位-----------------------3 级--------
11 – 0 位最后 12 位就是页内偏移量。

  • 当 MMU 翻译一个虚拟地址的时候,首先根据页表基地址寄存器的物理地址找到第 0 级页表页,然后将虚拟地址的虚拟页号( 第 47 – 39 位 ) 作为页表项索引,读取第 0 级页表页中的相应页表项。该页表项中存储着下一级页表页的物理地址, MMU 按照类似的方式将虚拟地址的虚拟页号作为页表项索引。继续读取第一级页表页中的相应的页表项。
那么上图中所说的 TLB 又是什么呢?
多级页表可以显著地压缩页表大小,但是会让地址翻译时长的增加,多级页表结
构使得 MMU 在翻译虚拟地址的过程,需要依次查找多个页表页中的页表项。
而为了减少地址翻译的速度

 MMU 引入了 转址旁路缓存( Translation Lookaside Buffer, TLB)
加速地址翻译的重要硬件 : TLB
  • TLB 缓存了虚拟页号到物理页号的映射关系;我们可以把 TLB 简化成存储着键对值的哈希表。
  • 从上图我们可以看到,MMU 会先把虚拟页号作为 键 去查询 TLB 中的缓存项,如果未命中则再去多级页表中查询,反之则直接返回。
那么一个很有趣的问题就是,难道我们引入了 TLB 这种数据结构就可以获得大
幅的 TLB 命中吗?
 是的!!!,其实一个应用程序大多都只会访问类似的内存位置,就是说前一
 次被访问的内存地址,那么他周围的内存通常也会被多次的访问,由于内存访
 问的时空局部性,TLB 缓存项在将来可能会被多次查询,即发生 TLB 命中的
 可能性较大。
问题又来了
我们如何保证 TLB 中的数据 和 当前页表中的数据所对应呢 ?(一致性)
TLB 刷新
如果两个应用程序 A 和 B 使用了 相同的 虚拟地址 VA,但是对应不同的 物理地址,
1. 应用程序 A 访问 VA 时,TLB会缓存 VA 到 PA1的翻译,
2. 切换到应用程序 B 后,操作系统会更新 CPU 所使用的 页表基地址,当时当 B 访问 VA 时,CPU 如果依然从 TLB 中寻找 VA 的翻译,那么就会导致应用程序 B 的 VA 也被翻译成 PA1,那么就产生了错误的内存访问错误!
3. 这个问题的原因:
		- 页表其实已经发生了变化,但 TLB 却没有做出刷新操作。
		- 所以操作系统在更换页表( 切换应用程序 ) 需要主动刷新 TLB。
  • 但如果操作系统频繁地刷新 TLB ,那么应用程序执行后,总会发生 TLB 未命中的情况。
  • 我们是否可以避免 / 降低页表切换所带来的开销呢?
    • 这里举例 AArch64,它提供了 ASID ( Address Space IDentifier )功能,具体来说,操作系统可以为不同的应用程序分配不同的 ASID 作为应用程序的身份标签。再把该标签写入写入应用程序的页表基地址寄存器中的空闲位。
这里不多赘叙。
换页与缺页异常
如果虚拟页尚未被分配使用,那么页表中自然没有相应的物理页映射吗 ?
被分配的虚拟页在页表中一定有相应的物理页映射吗 ?
答案 是 否定的。
哈哈, 你一定大受震撼吧!
虚拟页被分配使用之后,在页表中依然可能没有映射到物理页。
  • 小凡是个喜欢玩 LOL 的同学,他的电脑只有 2 GB运行内存,可是它先打开了 QQ ,该软件占用了 0.5 GB内存,然后双击了 LOL,LOL 需要占用 3 GB 内存,
    在一阵卡顿之后,LOL 还是打开了。
  • 那么为什么这两个应用程序能够成功打开呢?
虚拟内存的换页机制就是为了满足这种场景所设计的。

换页机制的基本思想就是当我们的物理内存不够使用时,操作系统应该把若干物理页的内容分写到类似于磁盘这种容量更大且更加便宜的存储设备

  • 然后操作系统就可以回收这些物理页并继续使用。
    • 当操作系统需要将物理页P的内容写到磁盘上的一个位置时,并且在应用程序A的页表中去除虚拟页 V 的映射,同时记录该物理页被换到磁盘的对应位置。该过程称为把物理页 P 换出,物理页 P 就可以被操作系统回收,并且分配给别的应用程序使用。
    • 此时,虚拟页 V 就处于已经分配但是没有映射到物理内存的状态。
缺页异常
  • 当应用程序访问已经分配但未映射到物理内存的虚拟页时,就会触发缺页异常。
    • 此时 CPU 会运行操作系统预先设置的缺页异常处理函数( page fault handler ), 该函数会找到( 也可能是通过换页的方式 ) 一个空闲的物理页,将之前写道磁盘上的数据内容全部加载到该物理页中,并且在页表中填写虚拟地址到这一物理页的映射,该过程就是 换入 ( swap in )。之后,CPU 就可以回到 发生缺页异常的地方继续执行。
	 利用换页机制,操作系统可以把物理内存放不下的数据临时放到磁盘上,等
到需要的时候再放回到物理内存中,从而能够为应用程序提供超过物理内存容量
的内存空间。( 小凡可生气了,他的内存只有 2 GB,玩LOL 一卡一卡的)。
  • 由于换页机制过程中所带来的耗时的裁判操作,因此操作系统会引入预取机制来进行优化,
  • 预取机制:当发生换入操作时,预测哪些页即将被访问,提前将他们换入物理内存,从而减少缺页异常的次数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值