凯哥stack

计算机体系结构(三) MMU原理

MMU原理(原图)

MMU(Memory Management Unit),即内存管理单元,是现代CPU架构中不可或缺的一部分,MMU主要包含以下几个功能:

  • 虚实地址翻译
    在用户访问内存时,将用户访问的虚拟地址翻译为实际的物理地址,以便CPU对实际的物理地址进行访问。

  • 访问权限控制
    可以对一些虚拟地址进行访问权限控制,以便于对用户程序的访问权限和范围进行管理,如代码段一般设置为只读,如果有用户程序对代码段进行写操作,系统会触发异常。

  • 引申的物理内存管理
    对系统的物理内存资源进行管理,为用户程序提供物理内存的申请、释放等操作接口。

使用MMU带来的好处或者优势:

  • 提升物理内存的利用率

    • 物理内存按需申请,如代码段的内存在执行时进行映射和转换,进程fork后,t通过写时复制(Copy-On-Write)进行真正的物理内存分配。
    • 解决内存管理碎片化的问题,即在系统运行一段时间后,频繁的内存申请和释放会导致内存碎片化,无法申请到一块足够大的地址连续的内存。
  • 对内存地址的访问进行控制
    如上述代码段只读权限控制,多线程的栈内存之间的空洞页隔离可以防止栈溢出后改写其他线程的栈内存,不同进程之间的地址隔离等等。

  • 将进程的地址空间隔离
    不同进程之间可以使用相同的虚拟内存地址空间,而进程间的物理内存又可以做到隔离,这保证了进程的独立性同时,又简化了地址的访问方式,如在早期32位CPU上,为了支持4G以上的物理内存,一般物理地址有36-bit(如PowerPC-604系列),但是用户的虚地址仍然使用32-bit,做法就是将用户的不同进程的32-bit虚地址在MMU转换时,转换为36-bit的物理地址,这样每个进程仍然能访问0-3G虚地址范围,将多个进程的3G空间映射到36-bit的物理内存空间中去。

MMU的由来

  • Swap模式时代
    早期计算机在执行程序时,将程序从磁盘加载到内存执行中执行,在多用户系统中,当新的用户程序被执行前,需要先将当前用户的程序从内存swap到磁盘,然后从磁盘加载新的程序执行,当前用户退出后,在将前一用户程序从磁盘中加载到内存继续执行,每次用户切换伴随程序的swap,消耗较大。

    图1: Swap示意

  • Page模式时代
    后来人们将内存划分为固定大小的Page,一般为4K或者更小,这样用户程序按需以Page的方式加载到内存,不需要将整个程序加载到内存,这样内存可同时容纳更多程序,而无需按照用户切换进行swap,提升了内存利用率和加载时间(PageFault是Page时代的产物,而非MMU时代独有)。

    图2: Page模式

  • 动态地址转换(DAT - Dynamic Address Translation)
    最早可以追溯到1966年IBM研发的System/360-Model67,在该计算机的设计中首先引入了动态的地址转换机制,在Page模式基础上,为用户程序分配虚地址(VA),通过DAT转换为物理地址(PA)进行访问。通过好处是用户可以使用连续的地址,而不再受制于物理内存大小和Page碎片化的限制,原则上用户的程序只受磁盘大小限制,代价是增加虚实地址转换机制。

    • 实现方式
      采用segment、page、offset模式,将24-bit虚拟地址分为3段,0-7bit保留,8-11bit索引segment,一共16个segment,12-19bit索引page,每个segment最多256个page,20-31bit为page offset,每个用户有一个虚实地址映射表,分为2级,即segment表和page表,每个segment指向一个page表。

    • 转换过程
      当CPU访问一个虚地址时,先通过VA的8-11bit查找segment表得到page表,再根据12-19bit在page表中找到PA的page起始地址,加上20-31bit的page offset就得到实际的PA了。

这就是MMU最早的雏形了。


图3: 动态地址转换

现代MMU的实现

现代CPU的MMU的地址转换方式一般分为3种模式,即实地址模式、块地址转换、页地址转换。


图4: 现代MMU

  • 实地址模式
    即CPU状态位中MMU使能位清零,MMU处于关闭状态,此时CPU操作的地址不经过转换(VA=PA),直接作为物理地址进行访问,CPU上电时或者在异常入口时处于该状态,在该状态下可以访问任意物理内存,非常危险,一般操作系统在CPU上电后做完必要初始化以后便使能MMU,或者在异常处理的入口保存好必要信息后使能MMU。

  • 块地址转换
    或者成为固定的地址转换或静态配置的地址转换表,这种模式支持配置一些固定的内存地址映射(VPN->RPN),比如Linux Kernel加载的地址,以PowerPC604为例,0xC0000000这段地址开始的256M内存映射使用了该模式的转换,好处是这种配置转换速度快,一般在特定的寄存器中配置,没有页表查找过程,缺点是缺乏灵活性,一次配置永久使用。

  • 页地址转换
    类似于早期的动态地址转换DAT,即将VA的一部分bit用于索引segment,另外一部分bit用于索引PTE表,最终得到物理地址的Page起始地址,再加上最后12bit(4K)的Page offset得到真正的物理地址。

    相比早期的DAT,有以下优化:

  • 增加了PTE表的缓存TLB(Translation lookaside Buffer),有些处理器将ITLB(指令)和DTLB(数据)分开,以减少指令和数据之间的缓存冲突。

  • 支持更大的物理地址(36bit以上)或逻辑地址,如在PowerPC中,用以应对现代操作系统的多进程管理将32bit通过segment寄存器扩展为52bit的逻辑地址,然后通过hash函数得到key用来查找PTE,并最终转换为36bit物理地址,逻辑地址的扩展用于减少多进程之间的PTE冲突。

  • 支持多种PTE查找方式,如硬件查找和软件查找。

现在CPU的MMU地址转换流程如下:


图5: 现代MMU处理流程

主要分为几个阶段:

  • 用户进程访问虚存地址。
  • 触发TLB查找过程,该部分通过硬件完成(灰色背景),没有软件参与。
  • TLB miss场景下,查找PTE(粉色背景),该部分在不同CPU上实现不同,像X86都是硬件查找,PowerPC有些处理器使用软件查找,即在内核实现一个TLB miss的异常处理,可以灵活做到TLB查找。
  • Do Page Fault,分为几种情况:
    • 新申请内存第一次读写,触发物理内存分配
    • 进程fork后子进程写内存触发Copy-On-Write。
    • 非法内存读写,错误处理。

总结

本篇文章,简单的将MMU的功能和演进过程做了一个框架性的介绍,以及现代MMU的简易处理过程,其中详细的机制后续有机会再做详细介绍,比如多进程的TLB缓存处理,PTE的多级页表查找,NUMA,MMU虚拟化等,同时随着用户的需求场景不断变化,MMU的功能也会不断更新升级,比如虚拟机的出现,催熟了MMU虚拟化功能,未来异构的硬件和系统中,比如CPU和GPU配合,GPU虚拟化等等,MMU将如何演进?欢迎大家一起探讨。

参考资料:
[1] https://en.wikipedia.org/wiki/Memory_management_unit
[2] https://en.wikipedia.org/wiki/Memory_paging

凯哥stack

著作权归作者所有,禁止转载


专题:

本文发表于 2021-03-02,最后修改于 2021-03-02。

本站永久域名kaige86.com,也可搜索「 凯哥stack 」找到我。

期待关注我的 ,查看最近的文章和动态。


上一篇 « 打造极致省电的Linux Book

推荐阅读

Big Image