现代操作系统普遍采用虚拟内存管理(Virtual Memory Management)机制,这需要MMU(Memory Management Unit,内存管理单元) 的支持。有些嵌入式处理器没有MMU,则不能运行依赖于虚拟内存管理的操作系统。

虚拟地址和物理地址

首先引入两个概念,虚拟地址和物理地址。如果处理器没有MMU,或者有MMU但没有启

用, CPU执行单元发出的内存地址将直接传到芯片引脚上,被内存芯片(以下称为物理内存,以便与虚拟内存区分)接收,这称为物理地址( Physical Address,以下简称PA) ,如下图所示:

MMU_虚拟地址

如果处理器启用了MMU, CPU执行单元发出的内存地址将被MMU截获,从CPU到MMU的地址称为虚拟地址( Virtual Address,以下简称VA) ,而MMU将这个地址翻译成另一个地址发到CPU芯片的外部地址引脚上,也就是将虚拟地址映射成物理地址,如下图所示:

MMU_物理地址_02 

注意,对于32位的CPU,从CPU执行单元这边看地址线是32条,可寻址空间是4GB,但是通常嵌入式处理器的地址引脚不会有这么多条地址线,因为引脚是芯片上十分有限而宝贵的资源,而且也不太可能用到4GB这么大的物理内存。事实上,在启用MMU的情况下虚拟地址空间和物理地址空间是完全独立的,物理地址空间既可以小于也可以大于虚拟地址空间,例如有些32位的服务器可以配置大于4GB的物理内存。我们说32位的CPU,是指CPU寄存器是32位的,数据总线是32位的,虚拟地址空间是32位的,而物理地址空间则不一定是32位的。物理地址的范围是多少,取决于处理器引脚上有多少条地址线,也取决于这些地址线上实际连接了多大的内存芯片。 

MMU将虚拟地址映射到物理地址是以页( Page) 为单位的,对于32位CPU通常一页为4KB。例如, MMU可以通过一个映射项将虚拟地址的一页0xb7001000~0xb7001fff映射到物理地址的一页0x2000~0x2fff,物理内存中的页称为物理页面或页帧( Page Frame) 。至于虚拟内存的哪个页面映射到物理内存的哪个页帧,这是通过页表( Page Table) 来描述的,页表保存在物理内存中, MMU会查找页表来确定一个虚拟地址应该映射到什么物理地址。总结一下这个过程:

1. 在操作系统初始化或者分配、释放内存时,会执行一些指令在物理内存中填写页表,然后

用指令设置MMU,告诉MMU页表在物理内存中的什么位置。

2. 设置好之后, CPU每次执行访问内存的指令都会自动引发MMU做查表和地址转换的操

作,地址转换操作完全由硬件完成,不需要用指令控制MMU去做。 

程序中的虚拟地址

我们在程序中使用的变量和函数都有各自的地址,程序被编译后,这些地址就成了指令中的地址,指令中的地址被CPU解释执行,就成了CPU执行单元发出的内存地址,所以在启用MMU的情况下,程序中使用的地址都是虚拟地址。

由于有了虚拟内存管理机制,各进程不必担心自己使用的地址范围会不会和别的进程冲突,比如两个进程都使用了虚拟地址0x08048000,操作系统可以设置MMU的映射项把它们映射到不同的物理地址,它们通过同样的虚拟地址访问不同的物理页面,就不会冲突了。

MMU的内存保护机制

MMU除了做地址转换之外,还提供内存保护机制。各种体系结构都有用户模式( UserMode) 和特权模式( Privileged Mode) 之分,操作系统可以设定每个内存页面的访问权限,有些页面不允许访问,有些页面只有在CPU处于特权模式时才允许访问,有些页面在用户模式和特权模式都可以访问,允许访问的权限又分为可读、可写和可执行三种。这样设定好之后,当CPU要访问一个VA时, MMU会检查CPU当前处于用户模式还是特权模式,访问内存的目的是读数据、写数据还是取指令,如果和操作系统设定的页面权限相符,就允许访问,把它转换成PA,否则不允许访问,产生一个异常( Exception) 。异常的处理过程和中断类似,只不过中断是由外部设备产生的,而异常是由CPU内部产生的,中断产生的原因和CPU当前执行的指令无关,而异常的产生就是由于CPU当前执行的指令出了问题,例如访问内存的指令被MMU检查出权限错误,除法指令的除数为0等。通常操作系统把虚拟地址空间划分为用户空间和内核空间,例如x86平台的虚拟地址空间是0x0000 0000~0xffff ffff,大致上前3GB( x0000 0000~0xbfff ffff)是用户空间,后1GB( 0xc000 0000~0xffff ffff)是内核空间。用户程序在用户模式下执行,不能访问内核中的数据,也不能跳转到内核代码中执行。这样可以保护内核,如果一个进程访问了非法地址,顶多这一个进程崩溃,而不会影响到内核和其它进程。 CPU在产生中断或异常时会自动切换模式,由用户模式切换到特权模式,因此跳转到内核代码中执行中断或异常服务程序就被允许了。事实上,所有内核代码的执行都是从中断或异常服务程序开始的,整个内核就是由各种中断处理和异常处理程序组成。

访问权限也是在页表中设置的,可以设定哪些页面属于用户空间,哪些页面属于内核空间,哪些页面可读,哪些页面可写,哪些页面的数据可以当作指令执行等等。 MMU在做地址转换时顺便检查访问权限。