Hi,早啊!让我看看周末还在偷偷学习卷我的人是谁?【狗头】
最近一直在学习内存管理,也知道MMU是管理内存的映射的逻辑IP,还知道里面有个TLB。
在这里插入图片描述
今天刚刚好看到了几篇前辈的文章,很是不错,于是这里来一起学习一下吧。
MMU(Memory Management Unit,内存管理单元)是一种硬件模块,用于在CPU和内存之间实现虚拟内存管理。
其主要功能是将虚拟地址转换为物理地址,同时提供访问权限的控制和缓存管理等功能。
放在整个大系统多核架构里面,每个处理器内置了MMU模块,MMU模块包含了TLB和TWU两个子模块。
在这里插入图片描述
地址空间是一个抽象的概念,由CPU体系架构的地址总线决定,一般等同于CPU的寻址范围、x位处理器中的x。地址空间一般分为 虚拟地址空间 和 物理地址空间 。
任何时候,计算机上都存在一个程序能够访问的地址集合,我们称之为地址空间。这个空间的大小由CPU的位数决定,例如一个32位的CPU,它的地址范围是0 ~ 0xFFFFFFFF(4G),而对于一个64位的CPU,它的地址范围为0 ~ 0xFFFFFFFFFFFFFFFF。
这个空间就是我们的程序能够产生的地址范围,我们把这个地址范围称为 虚拟地址空间 ,该空间中的某一个地址我们称之为虚拟地址。与虚拟地址空间和虚拟地址相对应的则是物理地址空间和物理地址,大多数时候我们的系统所具备的物理地址空间只是虚拟地址空间的一个子集。
举一个例子,对于一台内存为 256MB的 32bit x86主机来说,它的虚拟地址空间范围是 0 ~ 0xFFFFFFFF(4G),而物理地址空间范围是 0x000000000 ~ 0x0FFFFFFF(256MB)。
为什么需要这样的转换呢?其实这个就是现在多进程多线程、以及解决内存碎片化的途径。这里就不展开了。
虚拟地址又被简称为虚地址,物理地址又被称为实地址。虚拟地址和物理地址之间的转换,又称为虚实地址转化。
而这个转换的过程是硬件执行的:虚拟地址不是被直接送到内存地址总线上,而是送到内存管理单元MMU。他由一个或一组芯片组成,一般存在与协处理器中,其功能是把虚拟地址映射为物理地址。
在这里插入图片描述
内存管理单元(MMU)的一个重要功能是使系统能够运行多个任务,作为独立的程序运行在他们自己的 私有虚拟内存空间。
它们不需要了解系统的物理内存图,即硬件实际使用的地址,也不需要了解可能在同一时间执行的其他程序。
所以在这种时候其实也要注意,你到底是使用的物理内存还是虚拟内存,使用的同一片内存,会不会出现踩踏内存的现象。
在这里插入图片描述
你可以为每个程序使用相同的虚拟内存地址空间。
你也可以使用一个连续的虚拟内存地图,即使物理内存是碎片化的。
这个虚拟地址空间与系统中的实际物理内存地图是分开的。
你可以编写、编译和链接应用程序以在虚拟内存空间中运行。
如下图所示的内存虚拟和物理视图的系统实例,一个系统中的不同处理器和设备可能有不同的虚拟和物理地址图。
操作系统对MMU进行编程,在这两个内存视图之间进行转换。
在这里插入图片描述
要做到这一点,虚拟内存系统中的硬件必须提供地址转换,即把处理器发出的虚拟地址转换为主内存中的物理地址。
虚拟地址是你、编译器和链接器在内存中放置代码时使用的地址。
物理地址是由实际的硬件系统使用的。
MMU使用虚拟地址的最重要的位来索引映射表中的条目,并确定哪个块被访问。
MMU将代码和数据的虚拟地址映射成实际系统中的物理地址。
这种转换是在硬件中自动进行的,对应用程序是透明的。
除了地址转换外,MMU还控制内存访问权限、内存排序和每个区域内存的缓存策略。
(安全地址与非安全地址的访问控制权限,检查页标签)
在这里插入图片描述
MMU使任务或应用程序的编写方式要求它们对系统的物理内存图或可能同时运行的其他程序一无所知。这使你可以为每个程序使用相同的虚拟内存地址空间。
它还允许你使用一个连续的虚拟内存地图,即使物理内存是碎片化的。这个虚拟地址空间与系统中的实际物理内存地图是分开的。应用程序被编写、编译和链接以在虚拟内存空间中运行。
这个就回到了我之前说的这个MMU本质上提供的能力。
CPU发出的虚拟地址由两部分组成:V**和offset,V**(virtual page number)是页表中的条目number,而offset是指页内偏移。
最终转换后的物理地址也有两部分:PFN和offset,PFN( Physical frame number)是物理页框number,offset和上面虚拟地址的offset相同,是页内偏移。
在这里插入图片描述
MMU包含两个模块TLB(Translation Lookaside Buffer)和TWU(Table Walk Unit)。
TLB是一个高速缓存,用于缓存页表转换的结果,从而缩短页表查询的时间。
TWU是一个页表遍历模块,页表是由操作系统维护在物理内存中,但是页表的遍历查询是由TWU完成的,这样减少对CPU资源的消耗。
MMU由两部分组成:TLB(Translation Lookaside Buffer)和table walk unit。TLB 是一种地址转换cache,这里我们略过TLB的工作细节。
table walk unit在不同的CPU架构上有不同的叫法,但其作用是相同的,就是把内存页表走一走进行查表,完成虚拟地址到物理地址的转换。
在这里插入图片描述
TrustZone技术之所以能提高系统的安全性,是因为对外部资源和内存资源的硬件隔离。这些硬件隔离包括中断隔离、片上RAM和ROM的隔离、片外RAM和ROM的隔离、外围设备的硬件隔离、外部RAM和ROM的隔离等。
实现硬件层面的各种隔离,需要对整个系统的硬件和处理器核做出相应的扩展。这些扩展包括:
在支持TrustZone的SoC上,会对MMU进行虚拟化,使得寄存器TTBR0、TTBR1、TTBCR在安全状态和非安全状态下是相互隔离的,因此两种状态下的虚拟地址转换表是独立的。
存放在MMU中的每一条页表描述符都会包含一个安全状态位,用以表示被映射的内存是属于安全内存还是非安全内存。
虚拟化的MMU共享转换监测缓冲区(Translation Lookaside Buffer, TLB),同样TLB中的每一项也会打上安全状态位标记,只不过该标记是用来表示该条转换是正常世界状态转化的还是安全世界状态转化的。
Cache也同样进行了扩展,Cache中的每一项都会按照安全状态和非安全状态打上对应的标签,在不同的状态下,处理器只能使用对应状态下的Cache。
在REE(linux)和TEE(optee)双系统的环境下,可同时开启两个系统的MMU。在secure和non-secure中使用不同的页表.secure的页表可以映射non-secure的内存,而non-secure的页表不能去映射secure的内存,否则在转换时会发生错误:
在这里插入图片描述
在EL2系统中,MMU地址转换时,会自动使用TTBR2_EL1指向的页表。
在EL3系统中,MMU地址转换时,会自动使用TTBR3_EL1指向的页表。
在这里插入图片描述
如果启用了hypervisor那么虚拟地址转换的过程将有VA—>PA变成了VA—>IPA—>PA, 也就是要经过两次转换.在guestos(如linux kernel)中转换的物理地址,其实不是真实的物理地址(假物理地址),然后在EL2通过VTTBR0_EL2基地址的页表转换后的物理地址,才是真实的硬件地址。
在这里插入图片描述
IPA : intermediate physical address
在ARM嵌入式应用系统中, 很多系统控制由ARM CP15协处理器来完成的。CP15协处理器包含编号0-15的16个32位的寄存器。例如,ARM处理器使用C15协处理器的寄存器来控制cache、TCM(Tightly-Coupled Memory)和存储器管理。
在这些C15寄存器中和MMU关系较大的有C2、C7、C17寄存器。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Memory protection and control registers,内存保护和控制寄存器,包含
TTBR0、TTBR1是L1转换页表的基地址,
TTCR控制TTBR0和TTBR1的使用。
在这里插入图片描述
TLB 是 translation lookaside buffer 的简称。首先,我们知道 MMU 的作用是把虚拟地址转换成物理地址。
在这里插入图片描述
虚拟地址和物理地址的映射关系存储在页表中,而现在页表又是分级的。
64 位系统一般都是 3~5 级。
常见的配置是 4 级页表,就以 4 级页表为例说明。
分别是 PGD、PUD、PMD、PTE 四级页表。
在硬件上会有一个叫做页表基地址寄存器,它存储 PGD 页表的首地址。
在这里插入图片描述
MMU 就是根据页表基地址寄存器从 PGD 页表一路查到 PTE,最终找到物理地址(PTE页表中存储物理地址)。
这就像在地图上显示你的家在哪一样,我为了找到你家的地址,先确定你是中国,再确定你是某个省,继续往下某个市,最后找到你家是一样的原理。一级一级找下去。(这个比喻真的不错)
这个过程你也看到了,非常繁琐。如果第一次查到你家的具体位置,我如果记下来你的姓名和你家的地址。下次查找时,是不是只需要跟我说你的姓名是什么,我就直接能够告诉你地址,而不需要一级一级查找。
四级页表查找过程需要四次内存访问。延时可想而知,非常影响性能。页表查找过程的示例如下图所示。以后有机会详细展开,这里了解下即可。
在这里插入图片描述
TLB 其实就是一块高速缓存。
数据 cache 缓存地址(虚拟地址或者物理地址)和数据。TLB 缓存虚拟地址和其映射的物理地址。TLB 根据虚拟地址查找 cache,它没得选,只能根据虚拟地址查找。
所以 TLB 是一个虚拟高速缓存。硬件存在 TLB 后,虚拟地址到物理地址的转换过程发生了变化。虚拟地址首先发往 TLB 确认是否命中 cache,如果 cache hit 直接可以得到物理地址。
否则,一级一级查找页表获取物理地址。并将虚拟地址和物理地址的映射关系缓存到 TLB 中。
table walk unit:包含从内存中读取translation tables的逻辑
在这里插入图片描述
一个完整的页表翻译和查找的过程叫作页表查询(Translation Table Walk),页表查询的过程由硬件自动完成,但是页表的维护需要软件来完成。
页表查询是一个相对耗时的过程,理想的状态是TLB里缓存有页表转换的相关信息。当TLB未命中时,才会去查询页表,并且开始读入页表的内容。
page table
page table是每个进程独有的,是软件实现的,是存储在main memory(比如DDR)中的
Address Translation
因为访问内存中的页表相对耗时,尤其是在现在普遍使用多级页表的情况下,需要多次的内存访问,为了加快访问速度,系统设计人员为page table设计了一个硬件缓存 - TLB,CPU会首先在TLB中查找,因为在TLB中找起来很快。TLB之所以快,一是因为它含有的entries的数目较少,二是TLB是集成进CPU的,它几乎可以按照CPU的速度运行。
在这里插入图片描述
如果在TLB中找到了含有该虚拟地址的entry(TLB hit),则可从该entry中直接获取对应的物理地址,否则就不幸地TLB miss了,就得去查找当前进程的page table。这个时候,组成MMU的另一个部分table walk unit就被召唤出来了,这里面的table就是page table。
使用table walk unit硬件单元来查找page table的方式被称为hardware TLB miss handling,通常被CISC架构的处理器(比如IA-32)所采用。它要在page table中查找不到,出现page fault的时候才会交由软件(操作系统)处理。
与之相对的通常被RISC架构的处理器(比如Alpha)采用的software TLB miss handling,TLB miss后CPU就不再参与了,由操作系统通过软件的方式来查找page table。使用硬件的方式更快,而使用软件的方式灵活性更强。IA-64提供了一种混合模式,可以兼顾两者的优点。
如果在page table中找到了该虚拟地址对应的entry的p(present)位是1,说明该虚拟地址对应的物理页面当前驻留在内存中,也就是page table hit。找到了还没完,接下来还有两件事要做:
如果该虚拟地址对应的entry的p位是0,就会触发page fault,可能有这几种情况:
后面再进一步就是看看这个TLB中具体是怎么找的,在page table中又是怎么"walk",这部分就是具体的地址是怎么转换的,翻译的。
虚拟地址转换的具体实现就在下面的PART 二。
有了这些基础之后我们来看看MMU内存是怎么分配的。
这部分可以了解CPU访问的虚拟地址是怎么转换成物理地址,然后通过物理地址在cache、主存或者EMMC中获取相应的数据。
在这里插入图片描述
在这里插入图片描述
物理页面大小一级地址总线宽度不同,页表的级数也不同。以AArch64运行状态,4KB大小物理页面,48位地址宽度为例,页表映射的查询过程如图:
在这里插入图片描述
对于多任务操作系统,每个用户进程都拥有独立的进程地址空间,也有相应的页表负责虚拟地址到物理地址之间的转换。MMU查询的过程中,用户进程的一级页表的基址存放在TTBR0。操作系统的内核空间公用一块地址空间,MMU查询的过程中,内核空间的一级页表基址存放在TTBR1。当TLB未命中时,处理器查询页表的过程如下:
当然虚拟地址空间划分不只是如此。因为目前应用程序没有那么大的内存需求,所以ARM64处理器不支持完全的64位虚拟地址,实际支持情况如下。
虚拟地址的最大宽度是48位 内核虚拟地址在64位地址空间的顶部,高16位是全1,范围是[0xFFFF 0000 0000 0000,0xFFFF FFFF FFFF FFFF];
用户虚拟地址在64位地址空间的底部,高16位是全0,范围是[0x00000000 0000 0000,0x0000 FFFF FFFF FFFF];
高16位是全1或全0的地址称为规范的地址,两者之间是不规范的地址,不允许使用。
如果处理器实现了ARMv8.2标准的大虚拟地址(Large Virtual Address,LVA)支持,并且页长度是64KB,那么虚拟地址的最大宽度是52位。这个也就是大页表,可以提升内存与访问速度。
可以为虚拟地址配置比最大宽度小的宽度,并且可以为内核虚拟地址和用户虚拟地址配置不同的宽度。
转换控制寄存器(Translation Control Register)TCR_EL1的字段T0SZ定义了必须是全0的最高位的数量,字段T1SZ定义了必须是全1的最高位的数量,用户虚拟地址的宽度是(64-TCR_EL1.T0SZ),内核虚拟地址的宽度是(64-TCR_EL1.T1SZ)。(全是0是代表的用户,全是1代表的是内核。)
在ARM64架构的Linux内核中,内核虚拟地址和用户虚拟地址的宽度相同。
这是因为 Linux 内核将虚拟地址空间分为多个页面,并将这些页面映射到物理地址空间上,以实现内存隔离、保护和虚拟内存等功能。
没有 MMU,就无法实现这种映射,从而无法运行 Linux 系统。
RTOS可以运行在没有MMU的系统上,因为RTOS通常不需要进行内存保护和虚拟地址映射等高级特性。
相反,RTOS的设计侧重于实时性和低延迟,因此通常只需要简单的内存管理和任务调度即可。
这使得RTOS可以运行在许多嵌入式系统上,包括一些没有MMU的系统。
内存真的是一个非常非常有意思且丰富的东西,不是我这一篇能讲完的,这肯定还是差的蛮多的,文章有很多的内容来自于前辈的文章和博客。如果看完有一点点的收获,那肯定就是值了的。
后续希望能出一个结合源码与硬件的长文,把内存和大家一起学习透彻。
记得点赞、关注、转发、在看是我最大的鼓励!!!
周末愉快!!!
感谢一下前辈们的卓越文章与书籍。