ARM MMU页表框架 file:///C:\Users\郭晓娟\AppData\Local\Temp\ksohtml\wpsB227.tmp.png 先上一张arm mmu的页表结构的通用框图(以下的论述都由该图来逐渐展开): file:///C:\Users\郭晓娟\AppData\Local\Temp\ksohtml\wpsB238.tmp.jpgfile:///C:\Users\郭晓娟\AppData\Local\Temp\ksohtml\wpsB239.tmp.png 以上是arm的页表框图的典型结构:即是二级页表结构: 其中第一级页表(L1)是由虚拟地址的高12bit(bits[31:20])组成,所以第一级页表有4096个item,每个item占4个字节,所以一级页表的大小为16KB,而在第一级页表中的每个entry的最低2bit可以用来区分具体是什么种类的页表项,2bit可以区分4种页表项,具体每种页表项的结构如下: file:///C:\Users\郭晓娟\AppData\Local\Temp\ksohtml\wpsB249.tmp.jpg 简而言之L1页表的页表项主要有两大类: 第一大类是指向第二级页表(L2页表)的基地址; 第二类直接指向1MB的物理内存。 在L1页表中每个表项可以覆盖1MB的内存,由于有4096K个选项(item),所以总计可以覆盖4096K*1MB=4GB的内存空间。 具体对应到Linux,由于linux的软件架构是支持3级页表结构,而arm架构实际只有2级的页表结构,所以linux代码中的中间级页表的实现是空的。在linux代码中,第一级的页表的页目录表项用pgd表示,中间级的页表的页目录表项用pud表示(arm架构其实不需要),第三级的页表的页目录表项用pmd表示(由于中间pud是空的,所以pgd=pmd),另外目前arm体系的移动设备中RAM的page大小一般都是4KB/page,所以L1页表中的页表项都是指向fine page table的。 但在linux内核启动的初始化阶段,临时建立页表(initial page tables)以供linux内核初始化提供执行环境,这时L1的页表项使用的就是第二种页表项(section enty),他直接映射的是1M的内存空间。具体的可以参考arch/arm/kernel/head.S中的__create_page_tables函数,限于篇幅,这里就不展开说了。 针对这种section page translation,mmu硬件执行虚拟地址转物理地址的过程如下: file:///C:\Users\郭晓娟\AppData\Local\Temp\ksohtml\wpsB25A.tmp.jpg 以上在初始化过程使用的临时页表(initial page tables),在内核启动的后期会被覆盖掉,即在paging_init--->map_lowmem函数中会重新建立页表,该函数为物理内存从0地址到低端内存(lowmem_limit)建立一个一一映射的映射表。所谓的一一映射就是物理地址和虚拟地址就差一个固定的偏移量,该偏移量一般就是0xc0000000(呵呵,为什么是0xc0000000?) 说到这里引入一个重要的概念,就是与低端内存相对的高端内存,什么是高端内存?为什么需要高端内存?为了解析这个问题,我们假设我们使用的物理内存有2GB大小,另外由于我们内核空间的地址范围是从3G-4G的空间,并且前面也说到了,linux内核的低端内存空间都是一一映射的,如果不引入高端内存这个概念,全部都使用一一映射的方式,那内核只能访问到1GB的物理内存,但实际上,我们是需要内核在内核空间能够访问所有的4GB的内存大小的,那怎么做到呢? 方法就是我们不让3G-4G的空间都使用一一映射,而是将物理地址的[0x00,fix_addr](fix_addr<1GB)映射到内核空间虚拟地址[0x00+3G,fix_addr+3G],然后将[fix_addr+3G,4G]这段空间保留下来用于动态映射,这样我们可以通过这段虚拟地址来访问从fix_addr到4GB的物理内存空间。怎么做到的呢? |