本文讲解的内容是Processor如何访问内存,TLB Cache和MMU的在Processor中扮演的角色。涉及的硬件平台是Xilinx Zynq-7000,dual-core ARM® Cortex-A9 MPCore,架构是armv7,下面分别对TLB、MMU、Processor如何访问cache和主存深入分析。
在早期计算机系统中,程序员都是直接访问物理地址进行编程,当程序出现错误时,整个系统都瘫痪掉;或者在多进程系统中,当一个进程出现问题,对属于另外一个进程的数据或者指令区域进行写操作,会导致另外一个进程崩溃。于是虚拟地址就被提出,软件使用虚拟地址访问内存,而处理器负责虚拟地址到物理地址的映射工作(地址转换时靠MMU来完成)。处理器采用多级页表来进行多次查找最终找到真正的物理地址。当处理器发现页表中找不到真正对应的物理地址时,就会发出一个异常,挂起寻址错误的进程,但是其他进程仍然可以正常工作。从虚拟地址到物理地址的转换过程可知:由于页表是存放在内存中的,使用一级页表进行地址转换时,每次读/写数据需要访问两次内存,第一次访问一级页表获得物理地址,第二次才是真正的读/写数据;使用两级页表时,每次读/写数据需要访问三次内存,访问两次页表(一级页表和二级页表)获得物理地址,第三次才是真正的读/写数据。拿Cortex-A9处理器访问两级页表举例说明,当处理器拿到一个需要访问内存的虚拟地址A,首先查找MMU里面页表地址寄存器得到页表在内存中的物理地址,然后MMU通过访问内存控制器去访问内存中的两级页表得到A1、A2两个地址,A1和A2按照一定规则组合得虚拟地址A的物理地址B,然后处理器在通过访问物理地址B得到内存数据(访问物理地址B首先需要查询cache是否命中,cache部分在前面文章已经讲解,这里不再涉及)。内存控制器结构如图1所示,cpu寻址内存物理地址过程如图2所示,注意内存控制器在cpu内部,为了方便画图讲解才放到cpu外部。
图 1
图 2
有人可能会想把页表放在三级Cache中就不同频繁的访问内存了,这样有一个问题导致不能把页表放在cache中。处理器每当进行寻址操作都要进行一次映射工作,这使得处理器访问页表的频率非常得高,有可能一秒钟需要访问几万次。因此,即使 Cache 的命中率能够达到 99% 以上,也就是说不命中率有1%,那么不命中的概率每秒也有几百次,这会导致处理器在单位时间内访问内存(因为Cache 没有命中,只能访问内存)的次数增多,降低了系统的性能。
上述的地址转换过程大大降低了CPU的性能,有没有办法改进呢?程序执行过程中,所用到的指令、数据的地址往往集中在一个很小的范围内,其中的地址、数据经常多次使用,这称为程序访问的局部性。由此,通过使用一个高速、容量相对较小的存储器来存储近期用到的页表条目(段/大页/小页/极小页描述符),以避免每次地址转换时都到内存去查找,这样可以大幅度地提高性能。这个存储器用来帮助快速地进行地址转换,称为“转译查找缓存”(TLB Cache)。当CPU发出一个虚拟地址时,MMU首先访问TLB Cache,如果TLB Cache中含有能转换这个虚拟地址的描述符,则直接利用此描述符进行地址转换和权限检查;否则MMU访问页表(页表是在主存中)找到描述符后再进行地址转换和权限检查,并将这个描述符填入TLB Cache中(如果TLB Cache已满,则利用round-robin算法找到一个条目,然后覆盖它),下次再使用这个虚拟地址时就可以直接使用TLB Cache中的地址描述符了。
TLB是一个内存管理单元用于改进虚拟地址到物理地址转换速度的缓存,位于MMU中。当cpu对数据进行读请求时,CPU根据虚拟地址(前20位)到TLB中查找,TLB中保存着虚拟地址(前20位)和页框号(页框号可以理解为页表项)的对映关系,如果匹配到虚拟地址就可以迅速找到页框号,通过页框号与虚拟地址后12位的偏移组合得到最终的物理地址,这个查表过程由MMU硬件自动完成。在分页机制中,TLB中的数据和页表的数据关联,不是由处理器维护,而是由OS来维护, TLB的刷新是通过装入处理器中的CR3(用于保存页目录表页面的物理地址,仅高20位有效)寄存器来完成,MMU的基本功能如图3所示。
图3
一般初始化OS之前需要关闭数据Cache、MMU。Cache是通过CP15(包含16个32位的寄存器,这个需要查阅Cortex-A9 Datasheet)管理的,刚上电的时候CPU还不能管理Cache,指令cache关闭与否可以选择,但是数据cache必须关闭,否则最开始的代码可能从数据cache中取数据,而这时候OCMC(片内RAM)中的数据还没有到data cache,这将导致数据预取异常。同样在cpu上电之前还没有对mmu初始化,这时候也用不到MMU,为了避免影响启动时初始化,所以需要关闭MMU。Cortex-A9启动初始化的局部代码如图4所示。
CPU访问Cache时用物理地址还是虚拟地址在下篇文章中讲解,这个是Cache的工作原理决定,同时不同原理会带来不同的问题。
图 4