硬件篇之处理器的MMU

1.MMU简介

  MMU即内存管理单元,现在处理器(CPU)中用来管理虚拟内存、物理内存的控制线路。MMU主要负责虚拟地址转换为物理地址和提供硬件机制的内存访问权限检查(内存保护)。MMU使得每个用户程序有自己独立的地址空间,并且确保程序内存空间的完整性。顺便说一句,很多IO设备有IO MMU其功能和CPU MMU类似,这里就不再赘述。

2.虚拟地址(VA)、线性地址(MVA)和物理地址(PA)

(1)为什么需要虚拟地址

  早期的计算机或者现在使用8位/16位MCU(单片机)的嵌入式设备,程序是直接运行在物理内存上的,也就是说其程序运行时所访问的地址就是物理地址。这样的好处是操作简单,内存存取速度快,但其缺点也是显而易见的。

1)程序运行地址不确定,因为每次装载程序的物理地址可能不一样

2)内存使用率低,因为需要把一个程序全部装入内存才能运行

3)程序大小受限,程序所需内存不能大于物理内存

4)内存碎片浪费,程序需要使用连续的一块内存才能运行,大量的内存碎片浪费了

5)内存保护问题,多进程中一个进程可以轻易的破坏其他进程的数据

  于是人们就引入了虚拟地址和线性地址(修改后的虚拟地址)的概念来解决上述缺点。其中虚拟地址为了解决问题1),使得程序每次都运行在确定的虚拟地址空间里。 线性地址为了解决问题2)到5), 根据虚拟地址和线性地址的映射关系确定程序当前需要的地址,这样只需要把当前使用的部分装载进物理内存中,程序的大小也不受物理内存的约束。另外,通过线性地址和物理地址的映射,充分的利用了物理内存中的碎片空间,并通过引入内存访问权限检测机制确保了不同程序内存的完整性。

  由此可见,实现虚拟内存有两方面的工作:一是高效的管理线性地址和物理地址的映射关系;二是严格的内存访问权限检测机制。于是,现在的CPU处理器就引入了MMU硬件模块来处理这两个问题。

  在没有MMU的处理器中,CPU内部执行单元产生的内存地址信号就是物理地址,将被直接通过地址总线发送到芯片引脚,被内存芯片接收。在有MMU且启用的处理器中,CPU执行单元产生的地址信号是虚拟地址,其在被发送到内存芯片之前将被MMU截获,MMU会负责把虚拟地址翻译成物理地址,然后发到内存芯片地址引脚上。

(2)虚拟地址到线性地址的转化过程

    通俗来讲,MVA是除CPU核心外的其他部分看到的虚拟地址,VA与MVA的变化关系很简单,就是一个线性函数,以ARM cup 为例:如果VA<32M,需要使用进程标识号PID(通过读CP15的C13获得)来转换为MVA :

      if (VA < 32M) then

       MVA = VA | (PID << 25)

      else

       MVA = VA

(3)线性地址到物理地址的转化过程

  对于线性地址最简单的管理方法就是以单个地址为单位管理,建立一个线性地址到物理地址的映射表,某一时刻给出一个线性地址就查表得到对应的物理地址。如果某个线性地址还没有物理地址的映射,就找到一个空闲的物理地址或者选出一个要替换掉的物理地址进行映射。但这样管理很琐碎,程序运行往往需要一片连续的内存,这样大大降低了程序的性能。

  现在MMU通常采用分段和分页的方式管理虚拟内存。分页思想很容易理解,既然单个地址为单位管理太麻烦,我就以页为单位管理,所以的页其实就是一片连续的地址空间,最常见的也大小是4K。分页方式的优点是页长固定,因而便于构造页表、易于管理,且不存在外碎片。但分页方式的缺点是页长与程序的逻辑大小不相关,比如可能会出现一个程序的一部分在内存,另一部分在辅存的情况,这样就大大影响了程序的性能。于是就出现了分段的思想,段是按照程序的自然长度确定的可以动态改变的区域。

  这里我主要介绍分页的实现原理。下图是一个线性地址,以最常见的32位宽地址和4K大小页面为例,低十二位[11:0]能够表示连续的4K内存(2的12次方),表示了一个页面内的位偏移,所以MVA和PA的[11:0]是相同的不需要地址转换。MVA的高20位[31:12]不能直接用于地址存取,其是用来确定对应的PA的。

  对[31:12]简单的管理思路是直接建一个大的MVA到PA的映射表,但是这个表会有1M(2的20次方)个表项,也不利于不同大小页的管理和拓展。常见的分页管理实现都是采用二级页表的方式,下面我分别介绍ARM和x86处理器中MMU分页管理的实现方式。

1)ARM

  ARM MMU最多会用到两级页表,其中分段方式只用到一级页表(这里的段并不是分段内存管理,就是一个超大的页而已),分页方式用到两级。两级页表中一级页表中存储的是二级页表的物理起始地址(基址),二级页表中存储的是最终物理页地址的基址。值得说明的是,二级页表分为粗页表和细页表,分页的大小有大页(64KB),小页(4KB)和极小页(1KB)三种。前者描述的二级页表,后者描述的是物理页,这两个概念不要弄混。MVA到PA地址转换流程如下图,我会在下文详细描述。

  进行MVA到PA转换的第一步是找到一级页表的位置,ARM中用TTB base代表一级页表的物理起始地址(32位),将它写入协处理器CP15的寄存器C2(即页表基址寄存器)。这样直接读取C2就得到了一级页表的物理起始地址。 一级页表是连续的16K(2的14次方)物理地址,所以TB base中高18位[31:14]是有意义的位,存储了页表基址,后14位[13:0]永远为0。

  一级页表的每个描述符(表项内容)占4B(32位),那么16k的一级页表一共有4096个描述。其中每个描述符对应1MB的地址,整个一共能来表示4GB(2的32次方)的地址空间,这也是32位宽地址的全部空间。使用MVA地址的高12位[31:20]来索引一级页表(2^12=4096,对应4096个描述符),每个描述符存储它对应的1MB物理空间的起始地址(段),或者存储下一级页表的地址(二级页表)。一级描述符格式如下图:

ä¸çº§é¡µè¡¨æ述符

一级描述符具体含义:

最低两位:

0b00:无效

0b01:粗页表。表示该一级描述符指向的二级页表是粗页表,其中描述符的高22 位[31:10]是粗页表基址(低10位填充0后就是一个粗页表的起始物理地址),一个粗页表含256个条目,使用MVA的中间8位[19:12](2^8=256)来检索粗页表。粗页表的每个条目表示4KB大小的连续物理地址空间,一个粗页表能表示1MB(256*4K)的物理地址。

0b10:段。表示该一级描述符指向一段物理内存空间(这里的段并不是分段内存管理,就是一个超大的页而已)。 其中描述符的高12 位[31:20]为段基址(低20位填充0后就是一个段1M物理地址空间的物理地址)。使用MVA的低20位[19:0]在这1MB空间中寻址。所以,描述符的位[31:20]和MVA[19:0]构成了这个线性地址MVA对应的物理地址。

  以段的方式进行映射时,MVA到PA的转换过程如下图所示:

①页表基址寄存器位[31:14]和MVA[31:20]组成一个低两位为0的32位地址,MMU利用这个地址找到段描述符(一级描述符)

②取出段描述符的位[31:20](段基址),它和MVA[19:0]组成一个32位的物理地址(这就是MVA对应的PA)

段å°å转æ¢è¿ç¨

0b11:细页表。表示该一级描述符指向的二级页表是细页表,其中描述符的高20位[31:12]为细页表基址(低12位填充0后就是一个细页表的起始物理地址)。一个细页表含1024个条目,使用MVA的中间10位[19:10](2^10=1024)来检索细页表。细页表的每个条目表示1KB大小的连续物理地址空间,一个细页表能表示1MB(1024*1K)的物理地址。

  根据一级描述符可知以分页方式(大页(64KB),小页(4KB)或极小页(1KB))进行地址映射时,需要用到二级页表,二级页表有粗页表、细页表两种,二级页表描述符格式如下:

二级页表描述符:

最低两位:

0b00:无效

0b01:大页描述符。表示该二级描述符指向的物理页面是大页,其中描述符的高16 位[31:16]为大页基址(低16位填充0后就是一个大页的起始物理地址)。一个大页是64KB的连续物理地址空间,粗页表中的每个条目只能表示4KB物理空间,如果大页描述符保存在粗页表中,则连续16个条目都保存同一个大页描述符。类似的,细页表中每个条目只能表示1KB的物理空间,如果大页描述符保存在细页表中,则连续64个条目都保存同一个大页描述符。

   下面以保存在粗页表中的大页描述符为例,MVA到PA的转换过程如下图所示:

①页表基址寄存器[31:14]和MVA[31:20]组成一个低两位为0的32位地址,MMU利用这个地址找到粗页表描述符

②取出粗页表描述符的[31:10](即粗页表基址),它和MVA[19:12]组成一个低两位为0的32位物理地址,通过这个地址找到大页描述符

③取出大页描述符的[31:16](即大页基址),它和MVA[15:0]组成一个32位的物理地址,即MVA对应的PA

这里要注意,步骤②和③中,用于在粗页表中索引的MVA[19:12]、用于在大页内寻址的MVA[15:0]有重叠的4位MVA[15:12]。必须保证当位[15:12]从0b0000变化到0b1111时,步骤②得到的大页描述符相同,即二级粗页表中连续的16个大页描述符完全相同,所以粗页表中有连续16个条目保存同一个大页描述符。

大页å°å转æ¢è¿ç¨

0b10:小页描述符。表示该二级描述符指向的物理页面是小页,其中描述符的高20 位 [31:12]为小页基址(低12位填充0后就是一个小页的起始物理地址),一个小页是4KB的连续物理地址空间 。粗页表中每个条目表示4kb的物理空间,如果小页描述符保存在粗页表中,则只需要用一个条目来保存一个小页描述符。类似的,细页表中每个条目只能表示1kb的物理空间,如果小页保存在细页表中,则连续4个条目都保存同一个小页描述符。

  下面以保存在粗页表中的小页描述符为例,MVA到PA的转换过程如下图所示:

①页表基址[31:14]和MVA[31:20]组成一个低两位为0的32位地址,MMU利用这个地址找到粗页表描述符

②取出粗页表描述符[31:10](即粗页表基址),它和MVA[19:12]组成一个低两位为0的32位物理地址,用这个地址找到小页描述符

③取出小页描述符的位[31:12](即小页基址),它和MVA[11:0]组成一个32位物理地址(即MVA对应的PA)

小页描述符保存在细页表中的情况,地址转换过程和上面类似。

å°é¡µå°å转æ¢è¿ç¨

0b11:极小页描述符。表示该二级描述符指向的物理页面是极小页,其中描述符的高22 位 [31:10]为极小页基址(低10位填充0后就是一个极小页的起始物理地址),一个极小页是1KB的连续物理地址空间 。极小页描述符只能保存在细页表中,用一个条目来保存一个极小页描述符。

  极小页MVA到PA的转换过程如下图所示:

①页表基址寄存器[31:14]和MVA[31:20]组成一个低两位为0的32位地址,MMU通过这个地址找到细页表描述符

②取出细页表描述符[31:12](即细页表基址),它和MVA[19:10]组成一个低两位为0的32位物理地址,通过这个地址即可找到极小页描述符

③取出极小页描述符[31:10](即极小页基址),它和MVA[9:0]组成一个32位的物理地址(即MVA对应的PA)

ARM MMU线性地址管理总结:

1))物理内存有段(1M)、大页(64k)、小页(4K)、极小页(1K)四种管理方式

2)以段进行映射时只用到一级页表,通过MVA[31:20]得到一级页表描述符中的一段(1MB)的起始物理地址,MVA[19:0]是偏移地址,用来在段中寻址

3)以大页进行映射时,通过MVA[31:16]得到二级页表描述符中的一个大页(64KB)的起始物理地址,MVA[15:0]是偏移地址,用来在大页中寻址

4)以小页进行映射时,通过MVA[31:12]得到二级页表描述符中的一个小页(4KB)的起始物理地址,MVA[11:0]是偏移地址,用来在小页中寻址

5)以极小页进行映射时,通过MVA[31:10]得到二级页表描述符中的一个极小页(1KB)的起始物理地址,MVA[9:0]是偏移地址,用来在极小页中寻址

 

2)X86

  ARM MMU这么复杂主要是页表和物理页都有分类,组合起来情况就很多,很复杂了。相比之下,X86的就简单多了。32位宽地址的X86 也是最多用到两级页表,其中大页(1M、2M等)内存只用到一级页表,普通页(4K)内存用到两级页表。其中,两级页表中一级页表中存储的是二级页表的物理起始地址(基址),二级页表中存储的是最终物理页地址的基址。

  两级页表的描述符情况如下图所示,其中Page Directory Entry为一级页表描述符(页目录实体),Page Table Entry为二级页表描述符(页表实体)。X86的具体寻址方式和ARM一样,这里就不再赘述了。