当前位置:首页 > 技术学院 > 技术前线
[导读]在ARM64(ARMV8)架构中,内存管理单元(MMU)负责完成虚拟地址到物理地址的转换,页表是MMU实现地址转换的核心数据结构。ARMV8架构对页表设计做了重新设计,相比32位ARM架构,支持更大的地址空间、更灵活的粒度配置,同时层级式的页表结构兼顾了地址空间范围和内存占用。

在ARM64(ARMV8)架构中,内存管理单元(MMU)负责完成虚拟地址到物理地址的转换,页表是MMU实现地址转换的核心数据结构。ARMV8架构对页表设计做了重新设计,相比32位ARM架构,支持更大的地址空间、更灵活的粒度配置,同时层级式的页表结构兼顾了地址空间范围和内存占用。

对于从事ARMV8架构底层开发、操作系统移植的开发者来说,深入理解页表结构是理解内存管理的基础,也是解决内存访问异常、地址转换错误问题的核心前提。本文从ARMV8的地址空间特点出发,逐层解析页表结构设计,理清各级页表的作用、转换流程,结合典型配置梳理核心逻辑。

一、ARMV8地址空间基础

ARMV8架构定义了虚拟地址和物理地址的最大范围,目前主流实现支持48位地址空间,部分最新实现支持52位地址扩展,我们先以最常用的48位地址空间为例分析,这也是Linux、RT-Thread等主流操作系统最常用的配置。

1. 虚拟地址空间划分

ARMV8采用分级分页,48位虚拟地址被划分为4个段,每一级页表对应9位地址,加上最后一页的12位偏移,刚好 49+12 = 48位,刚好对应4级页表;如果是2级页表则对应29+212=42位,三级页表对应39+12=39位,根据配置不同层级不同。

ARMV8虚拟地址空间从设计上就分为两个区域:用户空间和内核空间,地址的最高位决定了地址属于哪个空间:

最高位是0:用户虚拟地址空间,范围是0x000000000000到0x00FFFFFFFFFFFFFF,共256TB;

最高位是1:内核虚拟地址空间,范围是0xFF0000000000到0xFFFFFFFFFFFFFFFF,同样256TB。

这种划分天然支持内核和进程地址空间隔离,每个用户进程拥有独立的页表,地址空间互不干扰,内核页表全局共享,符合现代操作系统设计。

2. 支持的页大小粒度

ARMV8架构支持两种页粒度配置:4KB和16KB,部分实现还支持64KB粒度,不同粒度对应不同的页表层级和地址划分:

4KB粒度:每个页表项占8字节(64位),一个页可以容纳 4096/8 = 512个页表项,刚好对应9位地址(2^9=512),因此每一级页表对应9位地址,4KB页偏移正好是12位,因此48位地址需要四级页表。

16KB粒度:每个页容纳 16384/8 = 2048个页表项,对应11位地址,48位地址结构为 11+11+11+15=48,同样是四级页表,页偏移15位对应16KB。

64KB粒度:每个页容纳 65536/8 = 8192个页表项,对应13位地址,48位地址结构为13+13+9+13=48,同样支持三级结构。

目前最常用的还是4KB粒度,这也是内核默认配置,因此我们下面的分析都基于4KB粒度、48位地址空间的四级页表配置,这是工业界最普遍的场景。

二、ARMV8四级页表结构的核心设计

ARMV8的四级页表从上到下被命名为PGD(Pagetable Global Directory,全局页目录)、PUD(Page Upper Directory,上层页目录)、PMD(Page Middle Directory,中层页目录)、PTE(Page Table Entry,页表项),每一级对应虚拟地址的9位地址段,用于索引下一级页表,最后一级PTE对应物理页的基地址。

整个地址转换过程本质就是:通过虚拟地址的不同段,逐级索引找到最终的物理页基地址,加上页内偏移得到最终物理地址。我们先把48位虚拟地址的分段拆开:

层级地址分段位数作用

PGD索引[47:39]9索引第一级PGD页表项

PUD索引[38:30]9索引第二级PUD页表项

PMD索引[29:21]9索引第三级PMD页表项

PTE索引[20:12]9索引第四级PTE页表项

页偏移[11:0]12物理页内的字节偏移

这个划分正好对应四级页表结构,每一级索引正好找到对应表项,整个结构我们逐层拆解分析:

1. 第一级:PGD全局页目录

PGD是页表的最高层级,每个进程的地址空间对应一个PGD,PGD本身占用一个4KB的物理页,一共512个表项,每个表项占8字节,刚好填满一个页。

PGD每个表项保存的是下一级PUD页表的物理基地址,加上对应的标志位,比如有效位、访问权限、缓存属性等。虚拟地址的高9位作为索引,从PGD中取出对应表项,如果表项有效,就得到PUD页表的基地址。

对于ARMV8来说,TTBR0(转换表基地址寄存器0)保存用户空间的PGD基地址,TTBR1保存内核空间的PGD基地址,根据虚拟地址最高位自动选择对应寄存器,找到对应PGD,硬件会自动完成这个选择,不需要软件参与。

2. 第二级:PUD上层页目录

PUD是第二级页表,每个PUD也占用一个4KB物理页,同样512个表项,虚拟地址的第二个9位作为索引,从PUD中取出对应表项,得到下一级PMD页表的物理基地址。

PUD其实是随着地址空间扩展加入的层级,早期ARMV8只有三级页表,当支持48位地址后,扩展出了PUD层级,让地址空间划分更清晰,软件层面也和Linux内核的页表层级对齐,方便移植。

3. 第三级:PMD中层页目录

PMD是第三级页表,同样每个PMD占用一个4KB物理页,512个表项,虚拟地址的第三个9位作为索引,取出PMD表项,得到最后一级PTE的物理基地址。

这里需要提到块映射的优化:ARMV8支持在PMD级别直接映射大页,不需要再拆分到PTE,比如PMD直接映射2MB大小的内存块,这样就省去了最后一级PTE,节省了页表内存,对于连续大内存区域(比如内核代码段、大块DMA缓冲区),使用块映射可以大幅减少页表占用,提升TLB命中率。

块映射的判断很简单:只要PMD表项的有效位和块位都被置位,就说明这个表项直接指向物理块,不需要继续查下一级,直接用基地址加上低21位偏移就能得到物理地址,非常高效。

4. 第四级:PTE页表项

PTE是最后一级页表,每个PTE同样占用一个4KB物理页,512个表项,虚拟地址的第四个9位作为索引,取出最终的PTE表项,PTE表项保存了物理页的基地址。

得到物理页基地址后,加上虚拟地址低12位的页内偏移,就得到了最终的物理地址,整个地址转换过程完成。和PMD一样,PTE也支持块映射,PTE直接映射4KB页,就是最细粒度的映射。

三、ARMV8页表项的标志位设计

ARMV8的页表项是64位,除了保存下一级页表或者物理页的基地址,剩下的低12位用来存储标志位,这些标志位控制了内存的访问权限、缓存属性、安全状态,是MMU实现内存保护和缓存管理的核心。

以4KB粒度的页表项为例,标志位的定义如下(常用标志位):

标志位名称作用

bitValid有效位,置位表示表项有效,为0表示表项无效,访问会触发页错误

bitRead读权限,置位表示允许读访问,否则不允许

bitWrite写权限,置位表示允许写访问,否则不允许

bitExecute执行权限,置位表示允许执行代码,不允许表示该区域不可执行,防止溢出攻击

bitUser用户权限,置位表示用户空间可以访问,否则只有内核可以访问

bitnG非全局位,置位表示这个地址是进程私有,切换进程时TLB需要刷出

bitAF访问标志,硬件自动置位,表示该页被访问过,用于页面回收

bitSH共享属性,表示该页是否共享,多核系统维护缓存一致性用

bit[8:11]AttrIndx属性索引,指向MAIR寄存器中的内存属性,配置缓存策略(回写/直写/不缓存)

这些标志位实现了操作系统需要的内存保护机制:用户空间不能访问内核页面,只读页面不能写,数据区域不能执行,这些基础的内存隔离都依赖标志位实现。

值得一提的是ARMV8的AttrIndx设计,把内存缓存属性集中存在MAIR(内存属性索引寄存器)中,页表项只需要存4位索引,节省了页表项的空间,同时软件修改内存属性也更方便,只需要修改MAIR寄存器就能批量修改所有对应页的属性,设计非常巧妙。

四、地址转换流程:软硬件分工协作

ARMV8的地址转换主要由MMU硬件自动完成,软件只需要负责建立页表、修改页表项,整个转换流程对软件透明,我们梳理一次完整的虚拟地址到物理地址转换过程:

选择PGD基地址:CPU拿到虚拟地址后,根据最高位判断是用户地址还是内核地址,用户地址从TTBR0寄存器得到PGD基地址,内核地址从TTBR1得到PGD基地址。

PGD索引得到PUD基地址:取出虚拟地址[47:39]共9位,乘以8(每个表项8字节)加上PGD基地址,得到PGD表项的物理地址,读取PGD表项,如果表项无效,触发页错误异常;有效则取出PUD页表的物理基地址。

PUD索引得到PMD基地址:取出虚拟地址[38:30]共9位,乘以8加上PUD基地址,得到PUD表项地址,读取PUD表项,无效则触发页错误,有效则取出PMD页表物理基地址。

PMD索引判断类型:取出虚拟地址[29:21]共9位,乘以8加上PMD基地址,得到PMD表项地址,读取PMD表项:如果是块映射,直接得到块物理基地址,跳转到第6步合并偏移;如果是普通表项,取出PTE页表物理基地址。

PTE索引得到页基地址:取出虚拟地址[20:12]共9位,乘以8加上PTE基地址,得到PTE表项地址,读取PTE表项,无效触发页错误,有效得到物理页基地址。

合并页内偏移得到最终物理地址:取出虚拟地址低12位偏移,加上物理页(块)基地址,得到最终的物理地址,完成转换。

整个流程都是硬件自动完成,软件只需要在触发页错误的时候处理缺页、权限错误等异常,完成页表的修复,不需要参与每一次地址转换,地址转换效率很高。

为了加速地址转换,MMU集成了TLB(转译查找缓存),缓存最近常用的虚拟地址到物理地址的转换关系,大多数时候不需要遍历四级页表,直接查TLB就能得到结果,只有TLB未命中的时候才会遍历页表,大幅提升了转换性能。

五、地址扩展与两级/三级页表配置

除了我们上面分析的四级页表,ARMV8也支持更小地址空间的三级或者两级页表配置,满足不同场景需求:

1. 三级页表配置(39位地址空间)

如果只需要512GB的地址空间,可以用三级页表,地址划分是9+9+9+12=39位,省去了第一级PGD,直接把TTBR指向PUD,相当于把PGD合并到了基地址,适合资源有限的嵌入式场景,不需要太大地址空间,同时节省一级页表的内存开销。

2. 52位地址扩展的页表调整

最新的ARMV8架构支持52位物理地址和虚拟地址扩展,对于4KB粒度来说,52位地址划分调整为:9+9+9+9+16=52,依然是四级页表,最后页偏移是16位对应64KB粒度?不对,实际52位扩展下,依然保持四级结构,每一级还是9位,最后页偏移16位对应64KB,这样总共有4*9+16=52位,支持4PB的地址空间,满足更大内存的需求。

3. 合并页表的内存优化

ARMV8页表设计的灵活性在于,只有需要用到的页表层级才需要分配物理页,比如一个用户进程只用到1GB内存,只需要分配一个PGD、一个PUD、两个PMD和512个PTE,总共只需要不到10个物理页,也就是不到40KB的内存就能支撑1GB地址空间,不会提前分配所有页表,极大节省了内存占用,这是分级页表相比单级页表最大的优势。

六、工程开发中的常见问题分析

1. 页表对齐问题

ARMV8要求每一级页表必须和页大小对齐,也就是PGD、PUD、PMD、PTE都必须放在4KB对齐的物理地址上,如果地址不对齐,MMU会读取错误的基地址,导致地址转换错误,触发异常,因此分配页表内存的时候一定要保证4KB对齐,这是新手最容易踩的坑。

2. 标志位配置错误导致访问异常

很多开发者开启MMU后立刻产生死锁,大部分原因是页表标志位配置错误:比如内核代码段没有开启执行权限,取指的时候就会触发权限异常;或者设备寄存器区域开启了缓存,导致设备寄存器读写错误,一定要根据内存类型配置正确标志位:普通内存开启回写缓存,设备寄存器配置为不缓存、禁止执行。

3. 大地址访问错误

当使用超过48位的物理地址时,需要开启52位地址扩展,否则页表项只能保存40位物理地址,无法访问更高地址的内存,会导致高地址内存访问错误,所以如果你的系统内存超过256TB,一定要开启架构的52位扩展支持,页表项使用新的格式保存物理地址。

4. TLB缓存一致性问题

修改页表之后,TLB中还缓存着旧的地址转换关系,必须刷新对应的TLB条目,否则会使用旧的转换关系,导致错误的物理地址访问,所以修改页表项后,一定要执行TLB刷新指令,这一点在多核系统中尤其重要,还需要广播刷新所有核心的TLB,保证缓存一致性。

总结

ARMV8的页表结构设计兼顾了大地址空间支持和内存占用效率,层级式的设计灵活适配不同场景,从小体积嵌入式系统到大型服务器都能满足需求。从原理上看,核心逻辑就是通过虚拟地址分段,逐级索引找到最终物理页基地址,标志位实现内存保护和缓存管理,硬件自动完成转换流程,软件只需要管理页表和处理异常,这种设计让ARMV8的内存管理兼顾了性能和灵活性,也是它能成为当前主流64位架构的核心原因。

理解页表结构,不仅能帮助我们解决底层开发中的MMU异常问题,也是理解操作系统虚拟内存管理的基础,操作系统的缺页中断、写时复制、内存隔离这些核心特性,都建立在页表结构的基础之上,对于ARMV8底层开发来说,掌握页表结构就掌握了内存管理的核心。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
换一批
延伸阅读

一、操作系统Operation System第一台计算机并没有操作系统,计算机工作采用手工操作方式,使用效率极其低下。随着计算机的性能越来越快,手工操作的慢速度和计算机的高速度之间形成了尖锐矛盾,人们迫切需要一套完整

关键字: ARM mmu system 操作系统operation 内存管理单元
关闭