一、虚拟地址空间划分
ARM64 架构采用 48 位虚拟地址空间(最大支持 52 位,但主流实现为 48 位),划分为两部分:
用户空间:0x0000_0000_0000_0000 ~ 0x0000_FFFF_FFFF_FFFF(256TB),供用户进程使用。
内核空间:0xFFFF_0000_0000_0000 ~ 0xFFFF_FFFF_FFFF_FFFF(256TB),由内核独占。
核心特点:
无高端内存:48 位寻址空间足够大(256TB),无需像 ARM32 那样动态映射高端内存。
统一线性映射:物理内存通过固定偏移量直接映射到内核空间,简化访问流程。
二、内核空间详细布局
内核空间按功能划分为以下区域(以 4KB 页大小 + 4 级页表配置为例):
1. 线性映射区(Normal Memory)
范围:0xFFFF_8000_0000_0000 ~ 0xFFFF_FFFF_FFFF_FFFF(128TB)。
作用:直接映射全部物理内存,虚拟地址与物理地址呈线性关系:
虚拟地址 = 物理地址 + PAGE_OFFSET(PAGE_OFFSET 通常为 0xFFFF_8000_0000_0000)。
特点:
用于内核代码段(.text)、数据段(.data)、堆等核心内存。
通过 kmalloc() 或 __get_free_page() 分配的内存来自此区域。
支持大页(2MB 或 1GB)映射,减少页表层级和 TLB 未命中率。
2. vmalloc 动态映射区
范围:0xFFFF_0000_0000_0000 ~ 0xFFFF_7BFF_BFFF_0000(约 126,974GB)。
作用:分配虚拟连续但物理离散的内存(如 DMA 缓冲区、动态模块加载)。
特点:
物理页通过 vmalloc() 动态分配,性能低于线性映射区。
典型用例:大块非连续内存需求或物理内存碎片化场景。
3. vmemmap 区
范围:0xFFFF_7BFF_C000_0000 ~ 0xFFFF_7FFF_C000_0000(4,096GB)。
作用:存储物理页的 struct page 元数据数组。
特点:
每个 struct page 描述一个物理页,总大小与物理内存容量相关。
默认使用稀疏内存模型(SPARSEMEM)优化内存占用。
4. PCI I/O 区
范围:0xFFFF_7FFF_AE00_0000 ~ 0xFFFF_7FFF_BE00_0000(16MB)。
作用:映射 PCI 设备的 I/O 内存空间。
特点:
通过 ioremap() 动态映射外设寄存器。
访问需遵循设备特定的内存序和缓存策略。
5. 模块加载区(Modules)
范围:0xFFFF_7FFF_C000_0000 ~ 0xFFFF_8000_0000_0000(64MB)。
作用:加载内核模块(.ko 文件)的代码和数据。
6. 固定映射区(Fixmap)
范围:0xFFFF_FFFE_0000_0000 ~ 0xFFFF_FFFF_FFFF_FFFF(约 2GB)。
作用:预留给固定用途的虚拟地址(如早期启动阶段的临时页表映射)。
特点:
虚拟地址固定,物理地址在运行时动态绑定。
常用于 MMU 启用前的内存初始化。
三、典型内存布局示例
以 QEMU 实验平台为例,ARM64 内核内存布局如下(通过 dmesg 或 /proc/iomem 查看):
用户空间:0x0000_0000_0000_0000 ~ 0x0000_FFFF_FFFF_FFFF (256TB)
内核空间: vmalloc : 0xFFFF_0000_0000_0000 ~ 0xFFFF_7BFF_BFFF_0000 (126,974GB) vmemmap : 0xFFFF_7BFF_C000_0000 ~ 0xFFFF_7FFF_C000_0000 (4,096GB) PCI I/O : 0xFFFF_7FFF_AE00_0000 ~ 0xFFFF_7FFF_BE00_0000 (16MB) Modules : 0xFFFF_7FFF_C000_0000 ~ 0xFFFF_8000_0000_0000 (64MB) 线性映射区 : 0xFFFF_8000_0000_0000 ~ 0xFFFF_FFFF_FFFF_FFFF (128TB)
四、关键机制与优化
线性映射优势:
直接通过偏移量访问物理内存,减少地址转换开销。
支持大页映射(如 2MB/1GB),降低页表层级和 TLB 未命中率。
连续映射标志(PTE_CONT):
若虚拟地址、物理地址及长度按 64KB 对齐,合并多个 PTE 项以减少 TLB 压力。
启动阶段内存初始化:
paging_init():初始化内核页表,建立线性映射和固定映射。
mem_init():移交内存管理权给 Buddy 分配器,释放早期启动内存。
五、与 ARM32 的对比
六、实际应用场景
驱动开发:外设寄存器通过 ioremap() 映射到 PCI I/O 区,需注意内存序和缓存策略。
性能调优:优先使用线性映射区分配内存(如 kmalloc()),减少 vmalloc 调用。
内存调试:通过 /proc/vmallocinfo 或 vmalloc 区日志分析动态内存分配。