2. ARM64 虚拟地址空间布局:用户空间与内核空间划分

ARM64 架构下,虚拟地址空间被划分为两个主要区域:用户空间(User Space)和内核空间(Kernel Space)。这种划分是操作系统实现进程隔离与内核保护的基础。Linux 内核在 ARM64 上采用经典的“3:1”或“1:1”分割模型,具体取决于配置的地址位宽。

2.1 地址空间大小:48位与52位

ARMv8.0 架构定义了 48 位虚拟地址(VA)和 48 位物理地址(PA)的支持。ARMv8.2 引入了 52 位地址扩展(LVA/LPA),允许使用 52 位虚拟地址和 52 位物理地址。Linux 内核通过配置选项选择支持的地址位数。

特性 48位地址空间 52位地址空间
虚拟地址宽度 48 bits 52 bits
总虚拟地址空间 256 TB 4 PB
用户空间大小 128 TB 2 PB
内核空间大小 128 TB 2 PB
页表层级 3级(4KB页)/ 2级(64KB页) 4级(4KB页)/ 3级(64KB页)
Linux 配置宏 CONFIG_ARM64_VA_BITS_48 CONFIG_ARM64_VA_BITS_52
关键点: 52 位地址空间需要硬件支持(ARMv8.2 及以上),并且需要启用 CONFIG_ARM64_VA_BITS_52。目前大多数通用硬件仍使用 48 位地址空间。

2.2 用户空间与内核空间的划分

在 ARM64 Linux 中,虚拟地址空间的高位决定区域归属:

  • 用户空间:地址范围从 0x0000_0000_0000_0000 到 0x0000_FFFF_FFFF_FFFF(48位)或 0x0000_FFFF_FFFF_FFFF_FFFF(52位)。
  • 内核空间:地址范围从 0xFFFF_0000_0000_0000 到 0xFFFF_FFFF_FFFF_FFFF(48位)或 0xFFFF_FFFF_FFFF_FFFF_FFFF(52位)。

内核空间与用户空间之间有一个巨大的“空洞”(Non-canonical 区域),用于捕获非法访问。

划分原则: 地址的最高有效位(MSB)决定空间归属。对于 48 位地址,如果 bit[47] = 0,则为用户空间;如果 bit[47] = 1,则为内核空间。对于 52 位地址,检查 bit[51]。
ARM64 虚拟地址空间布局(48位) 内核空间 (Kernel Space) 0xFFFF_0000_0000_0000 ~ 0xFFFF_FFFF_FFFF_FFFF 大小: 128 TB 非法区域 (Non-canonical 空洞) 用户空间 (User Space) 0x0000_0000_0000_0000 ~ 0x0000_FFFF_FFFF_FFFF 大小: 128 TB 虚拟地址 0x0000_0000_0000_0000 0xFFFF_FFFF_FFFF_FFFF

2.3 内核镜像映射区域

内核镜像(Kernel Image)被映射到内核空间的一个固定区域。在 ARM64 Linux 中,内核镜像映射区域通常位于内核空间的起始部分,称为 KIMAGE_VADDR。该区域包含内核代码段、数据段、初始化数据等。

内核镜像映射的关键特性:

  • 线性映射(Direct Mapping):内核镜像通常使用线性映射(也称为恒等映射或直接映射),即虚拟地址与物理地址之间存在固定的偏移量(PAGE_OFFSET)。
  • 模块区域:内核模块(动态加载的驱动)被映射到 MODULES_VADDR 区域,紧邻内核镜像。
  • vmalloc 区域:用于内核动态分配的非连续内存映射。
  • fixmap 区域:固定映射区域,用于早期引导或特殊用途。
区域名称 虚拟地址范围(48位) 用途
KIMAGE_VADDR 0xFFFF_8000_0000_0000 内核镜像映射(代码、数据)
MODULES_VADDR 0xFFFF_8000_0000_0000 + 内核大小 内核模块映射
VMALLOC_START 0xFFFF_8000_0000_0000 + 模块区域大小 vmalloc 动态映射
FIXADDR_START 靠近内核空间顶部 固定映射(如早期 I/O)
注意: 内核镜像映射区域的具体地址范围取决于内核配置(CONFIG_ARM64_VA_BITS)和内核大小。上述地址为典型值,实际运行时可通过 /proc/kallsyms 或内核日志查看。
ARM64 内核空间详细布局 内核空间 (0xFFFF_0000_0000_0000 ~ 0xFFFF_FFFF_FFFF_FFFF) KIMAGE_VADDR (内核镜像) 代码段 .text | 数据段 .data | BSS MODULES_VADDR (内核模块) 动态加载的驱动、文件系统模块 VMALLOC_START (vmalloc 区域) 内核动态非连续内存映射 线性映射 (Direct Mapping) 物理内存直接映射 (PAGE_OFFSET 偏移) FIXADDR_START (固定映射) 早期 I/O、调试、特殊用途映射

2.4 用户空间布局

用户空间布局由内核的 mmap 机制和 ELF 加载器决定。典型的用户空间地址布局包括:

  • 代码段(.text):从低地址开始加载(通常为 0x400000 或 0x555555554000)。
  • 数据段(.data, .bss):紧接代码段之后。
  • 堆(Heap):从数据段上方开始,向高地址增长。
  • 栈(Stack):从用户空间顶部(0x7FFFFF_FFFF_FFFF 附近)向低地址增长。
  • 共享库(mmap 区域):位于堆和栈之间,用于加载动态链接库。
ASLR(地址空间布局随机化): Linux 内核默认启用 ASLR,随机化堆、栈、mmap 基址和代码段加载地址,以增加攻击难度。可通过 /proc/sys/kernel/randomize_va_space 控制。

2.5 地址空间切换:用户态与内核态

当 CPU 在用户空间执行时,MMU 使用用户空间的页表(TTBR0_EL1)。当发生系统调用、异常或中断进入内核时,CPU 切换到内核空间,MMU 切换为使用内核空间的页表(TTBR1_EL1)。

执行模式 使用的页表基址寄存器 虚拟地址范围 特权级别
用户态(EL0) TTBR0_EL1 用户空间 (低地址) 无特权
内核态(EL1) TTBR1_EL1 内核空间 (高地址) 完全特权
硬件加速: ARM64 硬件通过 TCR_EL1(Translation Control Register)配置地址空间大小和页表属性。TCR_EL1 中的 T0SZ 和 T1SZ 字段分别控制用户空间和内核空间的地址位宽。