linux ARM64四级页表映射理解

本文探讨了48位虚拟地址配置下不同页面大小的内存管理机制,包括四级映射和section mapping的方法,并分析了各自所能覆盖的最大地址空间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://www.wowotech.net/memory_management/mm-init-1.html文章来源

 

假设48 bit的虚拟地址配置,4k的page size,这时候需要4级映射,地址被分成9(level 0 or PGD) + 9(level 1 or PUD) + 9(level 2 or PMD) + 9(level 3 or PTE) + 12(page offset),假设我们分配4个page分别保存Level 0到level 3的translation table,那么可以建立的最大的地址映射范围是512(level 3中有512个entry) X 4k = 2M。2M这个size当然不理想,无法容纳kernel image的地址区域,怎么办?使用section mapping,让PMD执行block descriptor,这样使用3个page就可以mapping 512 X 2M = 1G的地址空间范围。当然,这种方法有一点副作用就是:PAGE_OFFSET必须2M对齐。对于16K或者64K的page size,使用section mapping就有点不合适了,因为这时候对齐的要求太高了,对于16K page size,需要32M对齐,对于64K page size,需要512M对齐。不过,这也没有什么,毕竟这时候page size也变大了,不使用section mapping也能覆盖很大区域。例如,对于16K page size,一个16K page size中可以保存2K个entry,因此能够覆盖2K X 16K = 32M的地址范围。对于64K page size,一个64K page size中可以保存8K个entry,因此能够覆盖8K X 64K = 512M的地址范围。32M和512M基本是可以满足需求的。最后的结论:swapper进程(内核空间)需要预留页表的size是和page table level相关,如果使用了section mapping,那么需要预留PGTABLE_LEVELS - 1个page。如果不使用section mapping,那么需要预留PGTABLE_LEVELS 个page

此段的个人理解:

对于64位系统而言,一个entry是64位:
1个page 4k,那么可以保存512个entry(4*1024/8 = 512),一个entry可以指向一个4k的page,那么映射的范围为:512*4k = 2M
一个entry是64位,1个page 16k,16*1024/8 = 2k,那么可以保存2k个entry,需要11位虚拟地址来索引2k个entry
1个page 64k,那么可以保存8k个entry,64*1024/8 = 8k

另外:

1个page 4k,那么可以保存512个entry(4*1024/8 = 512),各个translation table的level为9,2^9 = 512,即虚拟地址中,就可以

用9位来索引512个entry了

 

16k 16384    14位
16384/8 = 2048 = 2k个entry 需要虚拟地址的11位来索引2k个entry

48        64k         16位页内偏移    每个entry 8个byte,共有8k个entry,  索引 8k entry 需要13个bit
            页表基地址        pgd/pud        pmd        pt        offset        level = 3
            16                 6            13         13        16

48        4k            12位页内偏移    每个entry 8个byte,共有512个entry,  索引 512个 entry 需要9个bit
            页表基地址        pgd        pud        pmd        pt        offset        level = 4
            16                 9        9        9         9        12

48        16k            14位页内偏移    每个entry 8个byte,共有2048个entry,  索引 2048 个 entry 需要11个bit
            页表基地址        pgd            pud        pmd        pt        offset        level = 4
            16                 1            11        11         11        14
    
47        16k            14位页内偏移    每个entry 8个byte,共有2048个entry,  索引 2048 个 entry 需要11个bit
            页表基地址        pgd    /pud    pmd        pt        offset        level = 3
            16                 11            11         11        14


47        4k            12位页内偏移    每个entry 8个byte,共有 512 个entry,  索引 512 个 entry 需要 9 个bit
            页表基地址        pgd    /pud    pmd        pt        offset        level = 3
            16                 9            9         9        12

ARM64架构中,页表项(Page Table Entry, PTE)用于描述虚拟地址到物理地址的映射关系,并包含访问权限、缓存属性等控制信息。以下是对ARM64架构下页表项的详细说明: ### 页表项的基本结构 ARM64支持多级页表机制,通常使用四级或五级页表结构,具体取决于系统的虚拟地址位数和配置。每一级页表中的条目都包含指向下一级页表的基地址以及相关的控制位。 #### 1. 页表项类型 ARM64页表项可以分为两类:**块描述符(Block Descriptor)** 和 **表描述符(Table Descriptor)**: - **块描述符**:直接描述一段连续的物理内存区域。 - **表描述符**:指向下一级页表的基地址。 #### 2. 页表项格式 一个典型的页表项长度为64位,其格式如下: | 位域 | 描述 | |-------------|----------------------------------------------------------------------| | [63:58] | 保留或扩展用途 | | [57:48] | 保留或用于软件定义 | | [47:16] | 物理地址高32位(对于4KB页面粒度,低16位为0) | | [15:2] | 属性字段,包括访问权限、缓存策略、执行权限等 | | [1] | 非安全状态位(NS) | | [0] | 有效位(Valid Bit),表示该页表项是否有效 | 其中,关键的控制位包括: - **Access Flag (AF)**:指示页面是否已被访问过[^2]。 - **Dirty Bit Management (DBM)**:用于管理页面是否被写入过[^2]。 - **Access Permissions (AP)**:定义页面的读写权限。 - **Non-secure (NS)**:标识页面是否属于非安全世界。 - **Execute Never (XN)**:禁止执行标志,防止代码在该页面上执行。 #### 3. 页表项的大小与对齐要求 每个页表项占用8字节(64位),并且必须按照8字节对齐。页表本身也必须满足特定的对齐要求,例如4KB对齐。 #### 4. 硬件更新访问和脏标志 ARMv8.1引入了硬件自动更新访问(Access)和脏(Dirty)标志的功能。当启用TCR_EL1寄存器中的HA(Hardware Access Flag Update)和HD(Hardware Dirty Bit Management)位时,硬件会自动处理这些标志位的更新,而无需触发异常。 ### 示例代码:查看页表项内容 以下是一个简单的内核模块示例,展示如何访问和解析ARM64架构下的页表项内容: ```c #include <linux/module.h> #include <linux/kernel.h> #include <asm/pgtable.h> static void print_pte(pte_t pte) { printk(KERN_INFO "PTE value: 0x%016llx\n", pte_val(pte)); if (pte_present(pte)) { printk(KERN_INFO " Present\n"); if (pte_young(pte)) printk(KERN_INFO " Accessed\n"); if (pte_dirty(pte)) printk(KERN_INFO " Dirty\n"); if (!pte_write(pte)) printk(KERN_INFO " Read-only\n"); } else { printk(KERN_INFO " Not present\n"); } } static int __init pte_init(void) { pgd_t *pgdp; pud_t *pudp; pmd_t *pmdp; pte_t *ptep; unsigned long vaddr = 0xfffffff000000000; // 虚拟地址示例 pgdp = pgd_offset_k(vaddr); if (pgd_none(*pgdp) || pgd_bad(*pgdp)) { printk(KERN_ERR "PGD not valid\n"); return -EINVAL; } pudp = pud_offset(pgdp, vaddr); if (pud_none(*pudp) || pud_bad(*pudp)) { printk(KERN_ERR "PUD not valid\n"); return -EINVAL; } pmdp = pmd_offset(pudp, vaddr); if (pmd_none(*pmdp) || pmd_bad(*pmdp)) { printk(KERN_ERR "PMD not valid\n"); return -EINVAL; } ptep = pte_offset_map(pmdp, vaddr); if (!ptep) { printk(KERN_ERR "PTE not mapped\n"); return -EINVAL; } print_pte(*ptep); pte_unmap(ptep); return 0; } static void __exit pte_exit(void) { printk(KERN_INFO "PTE module exited\n"); } module_init(pte_init); module_exit(pte_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple module to print PTE information"); ``` ### 总结 ARM64架构下的页表项设计支持高效的虚拟地址翻译,并提供了丰富的控制选项来管理内存访问权限和缓存行为。通过合理配置页表项,操作系统可以实现灵活的内存管理机制。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

科技之光666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值