本文都是假设系统是32位,页大小为4KB,基于ARM架构(不过和体系结构相关的内容不多)。
1. 了解MMU
在启用MMU的Linux内核中,CPU是通过虚拟地址来访问物理内存的。MMU(Memory Management Unit),即内存管理单元,它集成在CPU中,负责虚拟地址到物理地址的映射,以及物理地址的访问,并提供内存访问权限检查机制,以达到内存保护的作用(ARM里面的MPU(Memory Protection Unit)也可以做内存保护,但不能做虚拟内存映射)。
一旦启动MMU,CPU就只通过虚拟地址访问内存了。虚拟地址通过页表找到其映射的物理地址,前面已经讲过,根据页号和页内偏移找到物理地址。页表的起始地址要告知MMU,这样,CPU访存的时候,MMU拿到虚拟地址,查找页表找到其物理地址,然后访问(读/写等)物理内存或cache。
由于每个进程都有自己的虚拟地址空间,因此每个进程要有自己独立的页表。在进程切换时,就要向MMU更新页表的起始地址。在ARM中,页表的起始地址就存放在TTB(Translation table base)寄存器中,在进程切换时,通过$(CPU_NAME)_switch_mm(),如cpu_v7_switch_mm来更新页表的base pointer,实际上就是协处理器的操作。
如上所说,页表是有权限管理的,权限也存放于页表中,包括RWX权限和kernel/user+kernel权限(权限都是以页即4KB为单位的),例如你试图对const变量或代码段进行写操作,而页表中这些页的权限是只读,就会发生page fault,导致应用程序段错误。
当MMU找不到对应的物理地址或权限不对,都会触发page fault异常,当然不是所有pag