注意:请使用谷歌浏览器阅读(IE浏览器排版混乱)
【摘要】
本文主要介绍linux源代码中,是如何实现分页机制的。内存分页管理是arm架构中MMU的重要组成部分,理解它大有裨益。本文重点不在讲解理论知识,旨在通过源码的剖析,带你走进linux内存管理的世界。
【写作原因】
主要原因:后续介绍cache和缺页异常时都将以本文为根基进行展开.为以后介绍起来方便,专门写一篇文章.
【正文分析】
本文以linux3.18.20,armv7为例介绍.
一. 何时会创建页表?
首先谈一下用户常用的获取内存方式,以下几种常用的内存申请为例:
1) 内核态:kmalloc , kmalloc的内存从何而来?
kmalloc是从slab缓存区中申请的内存,而slab缓存区的内存实际上是从操作系统的低端内存申请到的。kmalloc过程其实并未创建页表,以后也不用再创建页表,这是因为系统启动过程已经通过map_lowmem->create_mappping为所有的低端内存创建好了页表。后文会对这一过程进一步描述。
2) 用户态:malloc
malloc:malloc过程其实大部分人都很了解,它主要通过brk或mmap拓展进程的虚拟地址空间,malloc过程本身并未申请物理内存,也未创建页表。可以理解为malloc申请了一段用户态的虚拟地址区间,然后当用户真正使用这段地址时,会触发arm的缺页异常,缺页异常处理函数handle_pte_fault会通过set_pte_at->set_pte_ext->cpu_v7_set_pte_ext 一系列调用 来实现页表创建。事实上,malloc申请的内存,来源也和kmalloc一样,也是来自低端内存。不过与kmaollc不同的是,缺页异常中对这段低端内存对应的物理内存,又做了二次映射,即重新创建了页表。发现了么,同一物理地址其实可以对应多个虚拟地址进行映射,即存在多个页表项,这其实也是多进程的实现基础,但同时,使用过程中也可能引入cache问题,可以参考我的另一篇介绍cache的博文.
ps:可以通过/proc/$pid/smaps或/proc/$pid/maps查看一个进程的地址空间.
3)高端内存:vmalloc
vmalloc实际上是为[VMALLOC_START,VMALLOC_END]地址区间创建页表.注意,我们申请到的低端内存对应的物理内存,不能再映射到其他的低端地址,却可以通过vmap映射到高端内存区[VMALLOC_START,VMALLOC_END].
4)io地址的映射:ioremap
驱动中经常使用ioremap 将io地址映射到指定区间,实际上,这一过程的本质也是创建页表.
以上几种申请内存的方式,最终都是通过cpu_v7_set_pte_ext配置页表项。
二 如何创建页表:
通过上面分析,我们了解了几种创建页表的时机,其实无论是kmalloc还是malloc,最后申请到的内存,都是通过cpu_v7_set_pte_ext来创建页表的。万法同源,我们以低端内存的映射为例,详细介绍下页表的创建.
.globl swapper_pg_dir
.epu swapper_pg_dir, KERNEL_RAM_VADDR - PG_DIR_SIZE;//页全局描述符起始虚拟地址:0x80004000
1)#grep "TEXT_OFFSET" ./ -r --exclude-dir=" *debug* "
-DTEXT_OFFSET:=0x0008000 --内核代码段在内核线性地址空间(0x80000000)中的偏移;
PAGE_OFFSET:物理地址从此开始映射,内核空间虚拟地址即线性地址的起始地址.
经常定义为0xc0000000或0x80000000,如下例子:
#define PAGE_OFFSET CONFIG_PAGE_OFFSET
- - 关键地址,用户态和内核态虚拟地址分界点,有的平台使用0xc0000000 (arch/arm/include/asm/memory.h)
#define CONFIG_PAGE_OFFSET 0x80000000 (autoconf.h)
- - 关键地址,用户态和内核态虚拟地址分界点,有的平台使用0xc0000000
2) 物理地址的起始地址 PHYS_OFFSET(如0x200000).该地址映射到线性地址起始地址处,即 PAGE_OFFSET .
PHYS_OFFSET+TEXT_OFFSET- PG_DIR_SIZE=0x204000
#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET) (arch/arm/kernel/head.s)
- - 0x80008000内核代码段起始虚拟地址,uImage代码段从此开始;页全局描述符地址,据此算出.
#define PG_DIR_SIZE=0x4000 (arch/arm/kernel/head.s)
ENTRY(stext)
bl __create_page_tables
ENDPROC(stext)
__create_page_tables
pgtbl r4,r8 /* r8=PHYS_OFFSET;r4=PHYS_OFFSET+TEXT_OFFSET-PG_DIR_SIZE 如:0x204000页全局目录起始物理地址 */
__create_page_tables创建初始页表,物理地址起始地址(如0x200000)映射到内核线性地址空间的起始地址(如0x80000000)
线性地址0x80004000(对应物理地址0x204000)上保存了页全局目录起始地址.
src/scripts/Makefile.lib :quiet_cmd_uimage= -a 0x208000 -e0x208000 其中0x200000是物理地址起始地址;
此处表明在uImage头中指定uImage代码段需要加载到的物理地址,boot中将uImage去头后读取到此地址,
内核态下0x208000映射到线性地址空间0x80008000即代码段所在物理地址对应的线性地址;
.text: 0x80008000-0x803cfea0 (3872kB--uImage代码段大小)
2 创建页表过程:
1)为低端内存创建页表:
setup_arch->paging_init->map_lowmem()->create_mapping
2)为io内存映射创建页表,ahb、apb等地址的映射。iotable_init->create_mapping
可见无论低端内存还是io地址,系统为他们创建页表项都是通过create_mapping
3 create_mapping介绍:
static void __init create_mapping(struct map_desc *md)
{
unsigned long addr, length, end;
phys_addr_t phys;
const struct mem_type *type;
pgd_t *pgd;
if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) {
printk(KERN_WARNING "BUG: not creating mapping for 0x%08llx"
" at 0x%08lx in user region\n",
(long long)__pfn_to_phys((u64)md->pfn), md->virtual);
return;
}
if ((md->type == MT_DEVICE || md->type == MT_ROM) &&
md->virtual >= PAGE_OFFSET &&
(md->virtual < VMALLOC_START || md->virtual >= VMALLOC_END)) {
printk(KERN_WARNING "BUG: mapping for 0x%08llx"
" at 0x%08lx out of vmalloc space\n",
(long long)__pfn_to_phys((u64)md->pfn), md->virtual);
}
/* mem_type中保存了页表属性和页中间目录的属性 */
type = &mem_types[md->type];
addr = md->virtual & PAGE_MASK;
/*phys对应物理地址,本函数实际上就是把phys映射到addr*/
phys = __pfn_to_phys(md->pfn);
length = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK));
if (type->prot_l1 == 0 && ((addr | phys | length) & ~SECTION_MASK)) {
printk(KERN_WARNING "BUG: map for 0x%08llx at 0x%08lx can not "
"be mapped using pages, ignoring.\n",
(long long)__pfn_to_phys(md->pfn), addr);
return;
}
/* 页表创建过程如下:*/
pgd = pgd_offset_k(addr);
end = addr + length;
do {
unsigned long next = pgd_addr_end(addr, end);
alloc_init_pud(pgd, addr, next, phys, type);
phys += next - addr;
addr = next;
} while (pgd++, addr != end);
}
注意:低端内存的页表属性已经在mem_types][MT_MEMORY_RW]中定义好。如果想要通过kamlloc获取到的内存是只读的,可以在此修改。
4 页表创建过程
static void __init create_mapping(struct map_desc *md)
{
/*
1 init_task进程的页全局目录地址:swapper_pg_dir
以后每个进程都从这里拷贝页全局目录到各自的pgd里dup_mm->pgd_alloc中实现,cpu_switch_mm中切换。
*/
pgd = pgd_offset_k(addr);
end = addr + length;
do {
unsigned long next = pgd_addr_end(addr, end);
/* 每个页全局目录 都要初始化页二级目录 */
alloc_init_pud(pgd, addr, next, phys, type);
phys += next - addr;
addr = next;
} while (pgd++, addr != end);
}
->
2 页二级目录初始化:
static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
unsigned long end, phys_addr_t phys,
const struct mem_type *type)
{
/*
页二级目录地址,因为不使用页二级目录,所以页二级目录地址等于页全局目录地址。
为实现软件兼容性所以代码中还保留了页二级目录的处理流程,只不过它的地址即是页全局目录
*/
pud_t *pud = pud_offset(pgd, addr);
unsigned long next;
do {
next = pud_addr_end(addr, end);
/*页三级目录初始化*/
alloc_init_pmd(pud, addr, next, phys, type);
phys += next - addr;
} while (pud++, addr = next, addr != end);
}
->
3页三级目录初始化:
static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
unsigned long end, phys_addr_t phys,
const struct mem_type *type)
{
/*
页三级目录地址,因为不使用页三级目录,所以页三级目录地址等于页二级目录,当然也等于页全局目录地址。
为实现软件兼容性所以代码中还保留了页三级目录的处理流程,只不过它的地址即是页全局目录
*/
pmd_t *pmd = pmd_offset(pud, addr);
unsigned long next;
do {
next = pmd_addr_end(addr, end);
/*if;else都有可能执行到*/
/* 当我们创建页表项的虚拟地址区间是1M对齐时 */
if (type->prot_sect && ((addr | next | phys) & ~SECTION_MASK) == 0) {
__map_init_section(pmd, addr, next, phys, type);
}
/* 当我们创建页表项的虚拟地址区间非1M对齐时 */
else {
alloc_init_pte(pmd, addr, next,__phys_to_pfn(phys), type);
}
phys += next - addr;
} while (pmd++, addr = next, addr != end);
}
->
4 页表初始化:
虚拟地址区间1M时,如下方式MMU映射:
static void __init __map_init_section(pmd_t *pmd, unsigned long addr,
unsigned long end, phys_addr_t phys,
const struct mem_type *type)
{
pmd_t *p = pmd;
do {
*pmd = __pmd(phys | type->prot_sect);
phys += SECTION_SIZE;
} while (pmd++, addr += SECTION_SIZE, addr != end);
flush_pmd_entry(p);
}
虚拟地址区间非1M对齐时,如下方式MMU映射,即创建页表:
static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,
unsigned long end, unsigned long pfn,
const struct mem_type *type)
{
/* 此时申请页表的地址(一个页表项4byte,一个pmd有512个页表项)页表的基地址会赋值给pmd ,__pmd_populate中完成赋值*/
pte_t *pte = early_pte_alloc(pmd, addr, type->prot_l1);
do {
/*为每一页配置页表属性,注意此时用到了mem_types定义的属性*/
set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)), 0);
pfn++;
} while (pte++, addr += PAGE_SIZE, addr != end);
}
5 页表属性配置函数:
无论内核态还是用户态,linux最后都是通过cpu_v7_set_pte_ext接口设置页表属性的:
ENTRY(cpu_v7_set_pte_ext)
/*
r0:页表项地址
r1:页表属性|物理地址偏移
将页表属性r1配置给页表项r0
*/
str r1, [r0] @ linux version
bic r3, r1, #0x000003f0
bic r3, r3, #PTE_TYPE_MASK
orr r3, r3, r2
orr r3, r3, #PTE_EXT_AP0 | 2
tst r1, #1 << 4
orrne r3, r3, #PTE_EXT_TEX(1)
eor r1, r1, #L_PTE_DIRTY
tst r1, #L_PTE_RDONLY | L_PTE_DIRTY
orrne r3, r3, #PTE_EXT_APX
tst r1, #L_PTE_USER
orrne r3, r3, #PTE_EXT_AP1
tst r1, #L_PTE_XN
orrne r3, r3, #PTE_EXT_XN
tst r1, #L_PTE_YOUNG
tstne r1, #L_PTE_VALID
eorne r1, r1, #L_PTE_NONE
tstne r1, #L_PTE_NONE
moveq r3, #0
ARM( str r3, [r0, #2048]! )
THUMB( add r0, r0, #2048 )
THUMB( str r3, [r0] )
ALT_SMP(W(nop))
ALT_UP (mcr p15, 0, r0, c7, c10, 1) @ flush_pte
bx lr
ENDPROC(cpu_v7_set_pte_ext)
以上函数实际上就是把页表属性配置到页表项中,C/B位也在此设置。其中页表项各字段含义如下:
注意:cpu_v7_set_pte_ext设置页表属性的时机一般为:
1) 驱动中io地址映射: ioremap->ioremap_pte_range->set_pte_ext
2) 用户空间mmap : mmap->mmap_mem->remap_pfn_range;
3) 系统初始化过程: map_lowmem/iotable_init->create_mapping
4) 等等,在此不逐一列举了。
5 在实际linux系统软件开发中如何修改页表属性,给出两种常见方式:
1) 可以通过配置全局变量mem_types属性修改低端内存cache的开启情况,create_mapping中真正使用该配置。内核在初始化中的会为低端内存、io地址等地址空间设置页表属性,如果要修改这一部分内存的属性包括cache开启关闭情况,mem_types中定义了初始页表项属性,形如:
prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY
其实在内核启动过程的build_mem_type_table()函数里,还会根据不同arm版本做调整。
2) 可以在映射一段物理内存之前通过系统的标志宏来修改cache使用情况。
举例:/dev/mem驱动中实现物理内存的重新映射(即mmap函数的实现过程).
mmap_mem->remap_pfn_range中:vm_page_prot = pgprot_writecombine(pVma->vm_page_prot);这段代码就是配置cache属性的。
内存映射前关闭cache的宏:
#define pgprot_noncached(prot) \
__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED)
#define pgprot_writecombine(prot) \
__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE)
关闭cache时使用的具体标志位:
#define L_PTE_MT_UNCACHED (_AT(pteval_t, 0) << 2) /* strongly ordered */
#define L_PTE_MT_BUFFERABLE (_AT(pteval_t, 1) << 2) /* normal non-cacheable */
【总结】
以上分析了linux系统实现分页机制的过程。为突出分页的主线,其他内存管理的知识直接给出了结论,未做源码上的细致分析。以后有时间可以详细介绍下linux系统的内存管理方法。
【拓展】
笔者曾经开发过两款调试工具:
一是可以通过命令查询一个内存地址是否打开cache。
二是可以查询一个地址被修改的情况,包括何时被修改,修改为何值等。
这两款调试工具的实现原理都和分页机制大有关联。
附录:
ARM中MMU工作原理 本文描述基于存储器管理单元的系统结构, 包含以下内容:
· 关于存储器管理单元的结构
· 存储器访问的顺序
· 转换过程
· 访问权限
· 域
· 异常
· CP15 寄存器
http://embedded.homeunix.org 30/06/2003
Page 3 of 3
3.1 关于存储器管理单元的结构
MMU 存储器系统的结构允许对存储器系统的精细控制。大部分的控制细节由存在存
储器中的转换表提供。这些表的入口定义了从1KB 到1MB 的各种存储器区域的属
性。这些属性包括:
虚拟地址到物理地址映射
ARM 处理器产生的地址叫虚拟地址,MMU 允许把这个虚拟地址映射到一个不
同的物理地址去。这个物理地址表示了被访问的主存储器的位置。
它允许用很多方式管理物理存储器的位置,例如:它可以用具有潜在冲突的
地址映射为不同的进程分配存储器,或允许具有不连续地址的应用把它映射
到连续的地址空间。
------注------
如果使用了快速上下文切换扩展(Fast Context Switch Extension),则在
本文中的虚拟地址的意思应该是修改过的虚拟地址(Modified virtual
address)
---------------
存储器访问权限(permissions)
这些控制对存储器区域的不可访问权限、只读权限、读写权限。当访问不可
访问权限的存储器时,会有一个存储器异常通知ARM 处理器。
允许权限的级别也受程序运行在用户状态还是特权状态影响,还受是否使用
了域有关。
高速缓存和缓冲位(Cachability and bufferability bits [C and B])
这些在高速缓存和缓冲一节讲
系统控制协处理器的寄存器允许对系统的高级控制,如转换表的位置。他们也用来
为ARM 提供内存异常的状态信息。
查找整个转换表的过程叫转换表遍历。它由硬件制动进行,并需要大量的执行时间
(至少一个存储器访问,通常是两个)。为了减少存储器访问的平均消耗, 转换表
http://embedded.homeunix.org 30/06/2003
Page 4 of 4
遍历结果被高速缓存在一个或多个叫作Translation Lookaside Buffers(TLBs)的
结构中。通常在ARM 的实现中每个内存接口有一个TLB。
· 有一个存储器接口的系统通常有一个唯一的TLB
· 指令和数据的内存接口分开的系统通常有分开的指令TLB 和数据TLB
如果系统有高速缓存, 高速缓存的数量也通常是由同样的方法确定的。所以在高
速缓存的系统中,每个高速缓存一个TLB。
当存储器中的转换表被改变或选中了不同的转换表(通过写CP15 的寄存器2),先
前高速缓存的转换表遍历结果将不再有效。MMU 结构提供了刷新TLB 的操作。
MMU 结构也允许特定的转换表遍历结果被锁定在一个TLB 中,这就保证了对相关的
存储器区域的访问绝不会导致转换表遍历,这也对那些把指令和数据锁定在高速缓
存中的实时代码有相同的好处。
3.2 存储器访问的顺序
当ARM 要访问存储器时,MMU 先查找TLB 中的虚拟地址表,如果ARM 的结构支持分
开的地址TLB 和指令TLB,那么它用:
· 取指令使用指令TLB
· 其它的所有访问类别用数据TLB
如果TLB 中没有虚拟地址的入口,则转换表遍历硬件从存在主存储器中的转换表中
获取转换和访问权限,一旦取到,这些信息将被放在TLB 中,它会放在一个没有使
用的入口处或覆盖一个已有的入口。关于转换表的信息和转换表遍历的实现参见转
换过程一节。
一旦为存储器访问的TLB 的入口被拿到,这些信息将被用于:
1. C(高速缓存)和B(缓冲)位被用来控制高速缓存和写缓冲,并决定是否高速
缓存。(如果系统中没有高速缓存和写缓冲,则对应的位将被忽略)
2. 访问权限和域位用来控制访问是否被允许。如果不允许,则MMU 将向ARM 处理
器发送一个存储器异常;否则访问将被允许进行。
访问权限、域和异常几节有详细描述。
http://embedded.homeunix.org 30/06/2003
Page 5 of 5
3. 对没有高速缓存的系统(包括在没有高速缓存系统中的所有存储器访问),物
理地址将被用作主存储器访问的地址。
对有高速缓存的系统,在高速缓存没有选中的情况下,物理地址将被用行取
(line fetch)的地址。如果选中了高速缓存,则物理地址将被忽略。
图3-1 说明了这种高速缓存系统
访问控
制硬件
TLB
ARM
处理器
高速缓
存和写
缓冲
转换表遍历
硬件
高速缓
存行取
硬件
主
存
储
器
虚拟地址
异常
域位
C, B位
物理地址
图3-1 高速缓存的MMU存储器系统
http://embedded.homeunix.org 30/06/2003
Page 6 of 6
3.2.1 允许和禁止MMU
通过写系统控制协处理器的寄存器1 的第0 位可以允许和禁止MMU。在复位后这位
是0,MMU 被禁止。
当MMU 被禁止时,存储器访问将被按如下处理:
1. 由具体的实现确定当MMU 被禁止时是否能够允许高速缓存和写缓冲。
· 当MMU 被禁止时不能允许高速缓存和写缓冲时,C 和B 位不起作用。
· 当MMU 被禁止时能允许高速缓存和写缓冲时:
i. 访问数据时被认为没有高速缓存和写缓冲(C==0,B==0)
ii. 取指令时:
a) 当系统只有一个唯一的TLB 时,认为是没有高速缓存。(C==0)
b) 当系统只有独立的指令TLB 时,认为是有高速缓存。(C==1)
2. 没有存储器访问权限的检查,MMU 也不产生异常信号。
3. 物理地址与虚拟地址相同(即所谓的平坦地址映射模式)。
在允许MMU 之前,必须在内存中建立适当的转换表,并且所有相关的CP15 寄存器
要被初始化正确。
注:-------------------
允许和禁止MMU 直接改变了虚拟地址到物理地址的映射(除非转换表被设定为平坦
地址映射模式)。所以很可能在允许MMU 时所有的高速缓存需要被刷新。
另外,如果允许MMU 的指令的物理地址和虚拟地址不同,取指令将变得复杂化。所
以,强烈建议允许MMU 的指令具有相同的物理地址和虚拟地址。
--------------------------
http://embedded.homeunix.org 30/06/2003
Page 7 of 7
3.3 转换过程
MMU 支持基于节或页的存储器访问:
节(Section) 构成1MB 的存储器块
支持3 中不同的页尺寸:
微页(Tiny page) 构成1KB 的存储器块
小页(Small page) 构成4KB 的存储器块
大页(Large page) 构成64KB 的存储器块
节和大页是支持允许只用一个TLB 入口去映射大的存储器区间。小页和大页有附加
的访问控制:小页分成1KB 的子页,和大页分成16KB 的子页。微页没有子页,对
微页的访问控制是对整个页。
存在主存储器内的转换表有两个级别:
第一级表 存储节转换表和指向第二级表的指针。
第二级表 存储大页和小页的转换表。一种类型的第二级表存储微页转换表。
MMU 把CPU 产生的虚拟地址转换成物理地址去访问外部存储器,同时继承并检查访
问权限。地址转换有四条路径。路径的选取由这个地址是被标记成节映射访问还是
页映射访问确定。页映射访问可以是大、小和微页的访问。
然而,转换过程总是由下面所描述的那样由第一级表的获取开始。节映射的访问只
需要读取第一级表,页映射的访问还需要读取第二级表。
3.3.1 转换表基址
当片上(on-chip)的TLB 中不包含被要求的虚拟地址的入口时,转换过程被启
动。转换表基址寄存器(CP15 的寄存器2)保存着第一级转换表基址的物理地址。
只有bits[31:14]有效,bits[13:0]应该是零(SBZ)。所以第一级表必须在16KB
的边界。
3.3.2 取第一级表
http://embedded.homeunix.org 30/06/2003
Page 8 of 8
转换表基址寄存器的bits[31:14]与虚拟地址的bits[31:20]和两个0 位连接形成
32 为物理地址,如图3-2。这个地址选择了一个四字节的转换表入口,它是第一级
描述符或是指向第二级页表的指针。
转换基址 SBZ
转换基址 表索引 00
31 14 13 0
31 20 19 0
31 14 13 2 10
表索引 xxxxxxxxxxxxxxxxxxxxxx
图3-2 访问转换表的第一级描述符
http://embedded.homeunix.org 30/06/2003
Page 9 of 9
3.3.3 第一级描述符
第一级表的每个入口是一个描述它所关联的1MB 虚拟地址是如何映射的描述符。见
表3-1,根据bits[1:0]的组合,有四种可能:
· 如果bits[1:0]==0b00,所关联的地址没有被映射,试图访问他们将产生一
个转换错(fault)。因为他们被硬件忽略,所以软件可以利用这样的描述
符的bits[31:2]做自己的用途。推荐为描述符继续保持正确的访问权限。
· 如果bits[1:0]==0b10,这个入口是它所关联地址的节描述符。见节描述符
和转换节参考中的细节。
· 如果bits[0]==1,这个入口给出粗糙第二级表(bit[1]==0),或精细第二
级表(bit[1]==1)。每一种类型的表描述了它所关联的1MB 存储区域的映
射。粗糙第二级表较小,每个表1KB,每个精细第二级表4KB。然而粗糙第
二级表只能映射大页和小页,精细第二级表可以映射大页、小页和微页。
3.3.4 节描述符和转换节参考
如果第一级描述符是节描述符,那么各个字段有如下的意义:
Bits[1:0] 描述符类型标识(0b10 表示节描述符)
Bits[3:2] 高速缓存和缓冲位
Bits[4] 由具体实现定义
Bits[8:5] 这个描述符控制的节的16 种域之一
Bits[9] 现在没有使用,应该为零
Bits[11:10] 访问控制,见表3-3
Bits[19:12] 现在没有使用,应该为零
Bits[31:20] 节基址,形成物理地址的高12 位
忽略 00
粗糙页表基址 sbz 域 imp 00
节基址 SBZ AP sbz 域 imp C B 10
精细页表基址 SBZ 域 imp 11
31 20 19 12 11 10 9 8 5 4 3 2 10
表 3-1 第一级描述符格式
错
粗糙页表
节
精细页表
http://embedded.homeunix.org 30/06/2003
Page 10 of 10
图3-3 表示了节转换的完整过程。
注:---------------
访问权限必须在物理地址产生之前去检查,检查访问权限的顺序见访问权限一节。
---------------------
表索引 节索引
31 20 19 0
虚拟地址
转换基址 SBZ
31 14 13 0
转换表基址
转换基址 表索引 00
31 14 13 2 1 0
第一级表地址
节基址 SBZ AP sbz 域 imp C B 10
31 20 19 12 11 10 9 8 5 4 3 2 1 0
第一级表描述符
节基址 节索引
31 20 19 0
物理地址
图3-3 节转换
First-level fetch
http://embedded.homeunix.org 30/06/2003
Page 11 of 11
3.3.5 粗糙页表描述符
如果第一级描述符是粗糙页表描述符,那么各个字段有如下的意义:
Bits[1:0] 描述符类型标识(0b01 表示粗糙页表描述符)
Bits[4:2] 由具体实现定义
Bits[8:5] 这个描述符控制的页的16 种域之一
Bits[9] 现在没有使用,应该为零
Bits[31:10] 页表基地址是一个指向第二极粗糙页表的指针,它给出第二级表访问
的基地址。而第二级粗糙页表必须在1KB 边界对齐。
如果从第一级读取到的是二级粗糙页表描述符,那么会象图3-4 所示执行第二级描
述符读取。
第一级表索引 第二级表索引 xxxxx
31 20 19 12 11 0
虚拟地址
转换基址 SBZ
31 14 13 0
转换表基址
转换基址 第一级表索引 0 0
31 14 13 2 1 0
第一级描述符地址
页表基址 sbz 域 imp 01
31 10 9 8 5 4 2 1 0
第一级描述符
页表基址 第二级表索引 00
31 10 9 2 1 0
第二级描述符地址
First-level fetch
图3-4 访问粗糙页表第二级描述符
http://embedded.homeunix.org 30/06/2003
Page 12 of 12
3.3.6 精细页表描述符
如果第一级描述符是精细页表描述符,那么各个字段有如下的意义:
Bits[1:0] 描述符类型标识(0b11 表示精细页表描述符)
Bits[4:2] 由具体实现定义
Bits[8:5] 这个描述符控制的页的16 种域之一
Bits[11:9] 现在没有使用,应该为零
Bits[31:10] 页表基地址是一个指向第二级精细页表的指针,它给出第二级表访问
的基地址。而第二级精细页表必须在4KB 边界对齐。
如果从第一级读取到的是二级精细页表描述符,那么会象图3-5 所示执行第二级描
述符读取。
第一级表索引 第二级表索引 xxxxx
31 20 19 10 9 0
虚拟地址
转换基址 SBZ
31 14 13 0
转换表基址
转换基址 第一级表索引 0 0
31 14 13 2 1 0
第一级描述符地址
页表基址 sbz 域 imp 01
31 12 11 9 8 5 4 2 1 0
第一级描述符
页表基址 第二级表索引 00
31 12 11 2 1 0
第二级描述符地址
First-level fetch
图3-5 访问精细页表第二级描述符
http://embedded.homeunix.org 30/06/2003
Page 13 of 13
3.3.7 第二级描述符
每个粗糙第二级表对映着以4KB 为单位的虚拟地址范围市怎么映射的,每个精细第
二级表对映着以1KB 为单位的虚拟地址范围市怎么映射的。那些入口是页描述符,
他们能够分别描述大于4KB 或1KB 的页。在这种情况下,这个描述符必须被重复足
够次,以保证这个页始终使用相同的描述符,不论访问这个页中的哪个虚拟地址。
对于一个第二级描述符,有四种可能,由描述符的bits[1:0]选择。见表3-2:
· 如果bits[1:0]==0b00,说关联的虚拟地址没有被映射,任何对这些虚拟地
址的访问将会导致转换错(fault)。软件可以利用这样的描述符的
bits[31:2]做自己的用途,因为他们被硬件忽略。推荐为描述符继续保持正
确的访问权限。
· 如果bits[1:0]==0b01,这个入口是大页描述符,描述64KB 的虚拟地址。
见转换大页参考。
一个大页描述符在精细第二级表中必须被重复64 次,在粗糙第二级表中必
须被重复16 次以保证所有的虚拟地址都被描述。
· 如果bits[1:0]== 0b10,这个入口是小页描述符,描述4KB 的虚拟地址。
见转换小页参考。
一个小页描述符在精细第二级表中必须被重复4 次,以保证所有的虚拟地址
都被描述。在粗糙第二级表中只有一个实例。
· 如果bits[1:0]== 0b11,这个入口是微页描述符,描述1KB 的虚拟地址。
见转换微页参考。
在精细第二级表中只需要一个微页描述符的实例。微页描述符不能在粗糙第
二级表中出现,如果出现了,结果不可预测。
忽略 00
大页基地址 SBZ AP3 AP2 AP1 AP0 C B 01
小页基地址 AP3 AP2 AP1 AP0 C B 01
微页基地址 SBZ AP C B 11
表3-2 第二级描述符格式
31 16 15 12 11 10 9 8 7 6 5 4 3 2 1 0
错
大页
小页
微页
http://embedded.homeunix.org 30/06/2003
Page 14 of 14
大页描述符字段
大页描述符的字段有如下意义:
bits[1:0] 表示描述符的类型
bits[3:2] 高速缓促和缓冲位
bits[11:4] 访问权限位。这些为控制对页的访问。关于这些位的解释见表3-3。
大页被分成4 各子页。
AP0 编码对第一个子页的访问权限。
AP1 编码对第二个子页的访问权限。
AP2 编码对第三个子页的访问权限。
AP3 编码对第四个子页的访问权限。
bits[15:12] 现在没有使用,应该为零。
bits[31:16] 用来形成物理地址的对应位。
小页描述符字段
小页描述符的字段有如下意义:
bits[1:0] 表示描述符的类型
bits[3:2] 高速缓促和缓冲位
bits[11:4] 访问权限位。这些为控制对页的访问。关于这些位的解释见表3-3。
小页被分成4 各子页。
AP0 编码对第一个子页的访问权限。
AP1 编码对第二个子页的访问权限。
AP2 编码对第三个子页的访问权限。
AP3 编码对第四个子页的访问权限。
bits[31:12] 用来形成物理地址的对应位。
微页描述符字段
微页描述符的字段有如下意义:
bits[1:0] 表示描述符的类型
bits[3:2] 高速缓促和缓冲位
bits[5:4] 访问权限位。这些为控制对页的访问。关于这些位的解释见表3-3 关
于微页的解释。
bits[9:6] 现在没有使用,应该为零。
bits[31:10] 用来形成物理地址的对应位。
http://embedded.homeunix.org 30/06/2003
Page 15 of 15
3.3.8 转换大页参考
图3-6 显示了在粗糙第二级表中转换一个64KB 的大页的完整顺序。在精细第二级
表中的转换顺序页相似,只是第二级描述符的地址如精细页表描述符一节所决定。
注:-----------------------------
页索引的高4 位和第二级表的低阶4 位重叠,在粗糙页表中大页的每个页表入口必
须被重复16 次。在精细页表中大页的每个页表入口必须被重复64 次。
-----------------------------------
大页基址 页索引
31 16 15 0
物理地址
大页基址 SBZ AP3 AP2 AP1 AP0 C B 0 1
31 16 15 121110 9 8 7 6 5 4 3 2 10
0
第二级描述符
页表基址 第二级表索引 00
31 10 9 2 10
第二级描述符地址
页表基址 sbz 域 imp 01
31 10 9 8 5 4 2 10
第一级描述符
转换基址 第一级表索引00
31 14 13 2 10
第一级描述符地址
转换基址 SBZ
31 14 13 2 10
转换表基址
第一级表索引 第二级表索引 页索引
31 20 19 16 15 12 11 0
虚拟地址
Second-level fetch
First-level fetch
图3-6 粗糙第二级表中的大页转换
http://embedded.homeunix.org 30/06/2003
Page 16 of 16
3.3.9 转换小页参考
图3-7 显示了在粗糙第二级表中转换一个4KB 的小页的完整顺序。在精细第二级表
中的转换顺序页相似,只是第二级描述符的地址如精细页表描述符一节所决定。
注:-----------------------------
当小页出现在精细第二级表中时,页索引的高2 位和第二级表的低阶2 位重叠,在
精细页表中小页的每个页表入口必须被重复4 次。
-----------------------------------
图3-7 粗糙第二级表中的小页转换
小页基址 页索引
31 12 11 0
物理地址
小页基址 AP3 AP2 AP1 AP0 C B 10
31 12 11 10 9 8 7 6 5 4 3 2 10
0
第二级描述符
页表基址 第二级表索引00
31 10 9 2 10
第二级描述符地址
页表基址 sbz 域 imp 01
31 10 9 8 5 4 2 10
第一级描述符
转换基址 第一级表索引 00
31 14 13 2 10
第一级描述符地址
第一级表索引 第二级表索引 页索引
31 20 19 12 11 0
虚拟地址
转换基址 SBZ
31 14 13 0
转换表基址
First-level fetch
Second-level fetch
http://embedded.homeunix.org 30/06/2003
Page 17 of 17
3.3.10 转换微页索引
图3-8 显示了在精细第二级表中转换1KB 微页的完整过程。
注:---------------------------
微页不能出现在粗糙第二级表中。
---------------------------------
转换基址 SBZ
31 14 13 0
转换表基址
第一级表索引 第二级表索引 页索引
31 20 19 10 9 0
虚拟地址
转换基址 第一级表索引 00
31 14 13 2 10
第一级描述符地址
页表基址 sbz 域 imp11
31 12 11 9 8 5 4 2 10
第一级描述符
微页表基址 SBZ AP C B 11
31 10 9 6 5 4 3 2 10
第二级描述符
页表基址 第二级表索引00
31 12 11 2 10
第二级描述符地址
微页表基址 页索引
31 10 9 0
物理地址
Second-level fetch
First-level fetch
图3-8 精细第二级表中的微页转换
http://embedded.homeunix.org 30/06/2003
Page 18 of 18
3.4 访问权限
在节和页描述符中的访问权限位控制对相应的节和页的访问。访问权限由CP15 的
寄存器1 的System(S)和ROM(R)位修改。表3-3 描述了访问权限位和S、R 位相互
作用时的意义。如果访问了没有访问权限的存储器空间,将会产生权限错(见异常
一节)。
表3-3 MMU 访问权限
AP
S
R
Privileged permissions
User permissions
0b00 0 0 不能访问 不能访问
0b00 1 0 只读 不能访问
0b00 0 1 只读 只读
0b00 1 1 不可预测 不可预测
0b01 X X 读/写 不能访问
0b10 X X 读/写 只读
0b11 X X 读/写 读/写
http://embedded.homeunix.org 30/06/2003
Page 19 of 19
3.5 域
域是节、大页和小页的集合。ARM 结构支持16 个域。对域的访问由域访问控制寄
存器的两个位字段控制。因为每个字段对访问对应的域的使能非常迅速,所以整个
存储器区间能很快地交换进出虚拟存储器。这里支持2 种域访问方式:
客户 域的用户(执行程序,访问数据),被形成这个域的节或页来监督访
问权限。
管理者 控制域的行为(域中的当前节和页,对域的访问),不被形成这个域
的节或页来监督访问权限。
一个程序可以是一些域的客户,也是另外一些域的管理者,同时没有对其它域的访
问权限。这允许对程序访问不同存储器资源的非常灵活的存储器保护。表3-4 说明
了域访问控制寄存器的位编码方式。
表3-4 域访问的值
值
访问方式
描述
0b00 不能访问 任何访问都将导致一个域错(domain fault)
0b01 客户 能否访问将根据节或页描述符中的访问权限位
确定
0b10 保留 使用这个值将导致不可预料的结果
0b11 管理者 不根据节或页描述符中的访问权限位确定能否
访问,所以不会产生权限错。(permission
fault)
http://embedded.homeunix.org 30/06/2003
Page 20 of 20