Linux 内核 ARM 虚拟内存布局全解析:从 4GB 地址空间划分到源码级验证

发布时间:2026/9/11 0:02:32
Linux 内核 ARM 虚拟内存布局全解析:从 4GB 地址空间划分到源码级验证 Linux 内核 ARM 虚拟内存布局全解析从 4GB 地址空间划分到源码级验证【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux导读本文以 Linux 内核仓库中 Documentation/arch/arm/memory.rst 为核心系统讲解 ARM 处理器上内核如何规划 4GB 虚拟地址空间从ffffffff顶端的固定映射区到0地址处的 NULL 指针陷阱页逐一剖析每一段地址区间的用途、边界常量与设计动机。文章不仅完整保留原文档的地址分配表还结合内核源码arch/arm/include/asm/下的头文件与arch/arm/mm/下的实现验证PAGE_OFFSET、VMALLOC_START、MODULES_VADDR、VECTORS_BASE等关键常量的真实定义与调用关系。读完本文你将能读懂任意 ARM 平台内核启动日志与/proc/iomem、/proc/vmallocinfo输出背后的地址语义并理解平台开发者为何不能随意占用这些保留区域。一、背景4GB 虚拟地址空间的三方共享ARM CPU32 位地址线最大可寻址 4GB 虚拟内存空间这部分空间必须在内核、用户态进程与硬件设备之间共享。这一约束是理解整个布局的核心前提用户态进程通过mmap()将线程/进程自身的映射放入低地址区内核需要直接映射物理 RAMlowmem、管理 vmalloc/ioremap 动态区、放置模块、映射固定设备地址等硬件设备外设寄存器通过ioremap()或静态映射iotable_init()进入内核虚拟地址空间。原文档特别强调随着 ARM 架构不断演进内核会持续为新特性保留更多 VM 空间因此该文档所描述的保留区会随时间增长。换言之这是一份活的契约平台代码必须随时与当前内核版本的布局保持一致。二、ARM Linux 内核虚拟内存布局总表下表完整继承自原文档是理解 ARM 内存布局的地图。表中所有地址均为虚拟地址VA按地址从高到低排列StartEnd用途ffff8000ffffffffcopy_user_page/clear_user_page使用区。对于 SA11xx 与 Xscale该区域用于建立 minicache 映射。ffff4000ffffffffARMv6 及之后 CPU 的 cache aliasing缓存别名处理区。ffff1000ffff7fff保留Reserved。平台不得使用该地址范围。ffff0000ffff0fffCPU 向量页vector page。若 CPU 支持向量重定位控制寄存器 V 位CPU 向量表映射于此。fffe0000fffeffffXScale cache flush 区。proc-xscale.S使用它刷写整个数据缓存XScale 没有 TCM。fffe8000fffeffffDTCM 映射区供 CPU 内部挂载 DTCM 的平台使用。fffe0000fffe7fffITCM 映射区供 CPU 内部挂载 ITCM 的平台使用。ffc80000ffefffffFixmap 映射区。fix_to_virt()提供的地址位于此区域。ffc00000ffc7ffffGuard 区域保护带。ff800000ffbfffff固件提供的 DT blob 的永久只读映射。fee00000feffffffPCI I/O 空间映射。这是 vmalloc 空间内的静态映射。VMALLOC_STARTVMALLOC_END-1vmalloc()/ioremap()空间。vmalloc/ioremap返回的内存动态放置于此机器相关的静态映射也通过iotable_init()放在这里。VMALLOC_START基于high_memory变量计算VMALLOC_END等于0xff800000。PAGE_OFFSEThigh_memory-1内核直接映射 RAM 区lowmem。典型情况下以 1:1 关系映射平台全部 RAM。PKMAP_BASEPAGE_OFFSET-1永久内核映射Permanent kernel mappings是把 HIGHMEM 页映射进内核空间的一种方式。MODULES_VADDRMODULES_END-1内核模块空间。通过insmod插入的内核模块使用动态映射放置于此。TASK_SIZEMODULES_VADDR-1使用 KASan 时的 shadow memory。该范围从MODULES_VADDR到内存顶部都被以每字节内存 1 比特的比例做了影子映射。00001000TASK_SIZE-1用户空间映射。线程/进程映射通过mmap()系统调用放置于此。0000000000000fffCPU 向量页 / NULL 指针陷阱。不支持向量重映射的 CPU 将向量页放在此处内核与用户空间的 NULL 指针解引用也由此映射捕获。⚠️原文档警告任何与上表区域冲突的映射都可能导致内核无法启动non-bootable或在内核运行时最终触发 panic。三、自高到低逐段解析每一段地址空间的源码依据3.1 顶部区域用户页拷贝 / minicache / cache aliasingffff8000 ~ ffffffffARM 将虚拟地址空间的最顶端ffff8000以上保留给copy_user_page/clear_user_page这类页级拷贝原语以及 ARMv6 的 cache aliasing 处理。在 arch/arm/include/asm/page.h 中可以看到copy_user_highpage/clear_user_highpage会根据 CPU 类型选择不同的实现v4wt、v4wb、v4_mc、xscale、xsc3、feroceon、fa等而针对 SA11xx 与 XScale 的 minicache 方案正是本文档此段注释的落地实现。arch/arm/mm/copypage-xscale.c 中可以看到#define minicache_pgprot __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | \ L_PTE_MT_MINICACHE)该文件注释说明在 SA11x0 与 XScale 处理器上拷贝用户页时会以特殊方式映射页面使访问不触碰主数据缓存、只进入 mini data cache从而避免在缺页处理时抖动thrashing主数据缓存。这就是ffff8000段被标注为用于建立 minicache 映射的底层原因。3.2 CPU 向量页VECTORS_BASEffff0000ffff0000~ffff0fff是 CPU 向量页。在 arch/arm/include/asm/memory.h 中有直接定义#define VECTORS_BASE UL(0xffff0000)若 CPU 支持向量重定位CP15 控制寄存器的 V 位异常向量表被映射到高地址0xffff0000否则向量页落在低地址0x0此时该页同时充当 NULL 指针陷阱页见下文 3.11 节。3.3 XScale cache flush 区与 TCM 映射fffe0000 ~ fffefffffffe0000~fffeffff供proc-xscale.S刷写整个数据缓存使用——因为 XScale 没有 TCM需要一段专用地址来做全缓存 flush。fffe8000~fffeffff与fffe0000~fffe7fff分别为 DTCM / ITCM 映射区。对应定义在 arch/arm/include/asm/memory.h#ifdef CONFIG_HAVE_TCM #define ITCM_OFFSET UL(0xfffe0000) #define DTCM_OFFSET UL(0xfffe8000) #endif原文档注释还给出了重要背景TCMTightly Coupled Memory固定为最大 32 KiB所以 ITCM 与 DTCM 各占一个 32 KiB 的地址窗口。3.4 Fixmap 映射区ffc80000 ~ ffeffffffix_to_virt()生成的固定映射地址位于ffc80000~ffefffff。源码定义在 arch/arm/include/asm/fixmap.h#define FIXADDR_START 0xffc80000UL #define FIXADDR_END 0xfff00000UL #define FIXADDR_TOP (FIXADDR_END - PAGE_SIZE)该文件中的enum fixed_addresses列举了此区域的固定槽位FIX_EARLYCON_MEM_BASEearly console、FIX_KMAP_BEGIN/END与KM_MAX_IDX * NR_CPUS相关的高端内存 kmap 槽位、FIX_TEXT_POKE0/1kprobes、jump labels 写只读内核文本、以及FIX_BTMAP_*early_ioremap 槽位与 kmap 区域共享因为early_ioremap()只在paging_init()之前可用而kmap()只在之后可用。固定映射的特点是地址在编译期/启动早期就确定不经过 vmalloc 分配器因此适用于 early console、kprobes 等需要在早期或原子上下文使用的场景。3.5 Guard 区域ffc00000 ~ ffc7ffffffc00000~ffc7ffff是 Guard 区域。这一保护带把 Fixmap 区与下方 DT blob 只读映射隔开用于捕获越界访问。类似的留洞设计也体现在 vmalloc 布局中在 arch/arm/include/asm/pgtable.h 中可以看到VMALLOC_OFFSET为 8MB且注释明确说明这是为了让物理内存结束后、内核虚拟内存开始前存在 8MB 空洞越界访问可以被及时发现vmalloc 各分配区间之间同样留有 4kB 空洞。3.6 固件 DT blob 永久只读映射ff800000 ~ ffbfffffff800000起是设备树DT blob的永久只读映射。定义在 arch/arm/include/asm/memory.h#define FDT_FIXED_BASE UL(0xff800000) #define FDT_FIXED_SIZE (2 * SECTION_SIZE) #define FDT_VIRT_BASE(physbase) ((void *)(FDT_FIXED_BASE | (physbase) % SECTION_SIZE))注意此处FDT_FIXED_BASE与下文 vmalloc 区终点VMALLOC_END同为0xff800000在数值上一致VMALLOC_END正是 vmalloc 区允许到达的最高地址其上的ff800000 ~ ffbfffff被划给 DT blob。启动早期setup_machine_fdt等路径即可在固定虚拟地址访问固件传入的 DT且映射属性为只读。3.7 PCI I/O 空间fee00000 ~ fefffffffee00000~feffffff是 PCI I/O 空间的静态映射位于 vmalloc 空间内部。它由iotable_init()一类接口建立。搜索 arch/arm/mm 可以看到iotable_init被 arch/arm/mm/dma-mapping.c 等文件调用说明机器相关静态映射含 PCI I/O统一走这一机制落入 vmalloc 区。3.8 vmalloc / ioremap 空间VMALLOC_START ~ VMALLOC_END-1这是内核最活跃的动态映射区。源码定义在 arch/arm/include/asm/pgtable.h#define VMALLOC_OFFSET (8*1024*1024) #define VMALLOC_START (((unsigned long)high_memory VMALLOC_OFFSET) ~(VMALLOC_OFFSET-1)) #define VMALLOC_END 0xff800000UL关键点VMALLOC_START不固定它基于high_memory计算并对齐到 8MBhigh_memory是直接映射 RAMlowmem的顶端因此平台 RAM 越大lowmem 越高vmalloc 区起点也随之抬高VMALLOC_END固定为0xff800000与 3.6 节 DT blob 映射的FDT_FIXED_BASE相接vmalloc()返回的动态映射、ioremap()建立的外设映射、以及平台通过iotable_init()建立的静态映射全部落在这段区间内每次vmalloc分配之间保留 4kB 空洞同样见 arch/arm/include/asm/pgtable.h 的注释用于捕获越界访问。3.9 内核直接映射 RAM 区lowmemPAGE_OFFSET ~ high_memory-1PAGE_OFFSET到high_memory-1是内核直接映射区平台 RAM 以 1:1 关系映射在这里。PAGE_OFFSET的定义在 arch/arm/include/asm/memory.h#define PAGE_OFFSET UL(CONFIG_PAGE_OFFSET) #define KERNEL_OFFSET (PAGE_OFFSET)CONFIG_PAGE_OFFSET由内核配置决定与用户/内核空间的分割比例VMSPLIT常见 2G/2G、3G/1G 等对应。这段区间内的虚拟地址与物理地址之间通过virt_to_phys()/phys_to_virt()互相转换其核心逻辑在 arch/arm/include/asm/memory.hstatic inline phys_addr_t __virt_to_phys_nodebug(unsigned long x) { return (phys_addr_t)x - PAGE_OFFSET PHYS_OFFSET; } static inline unsigned long __phys_to_virt(phys_addr_t x) { return x - PHYS_OFFSET PAGE_OFFSET; }当开启CONFIG_ARM_PATCH_PHYS_VIRT时内核会在启动时用.pv_table机制动态修正物理/虚拟偏移以支持物理内存不固定在0的平台。值得注意的约束同样写在该头文件中驱动不应直接使用virt_to_phys转换 DMA 地址DMA 地址转换应使用dma-mapping.h提供的接口。3.10 HIGHMEM 永久内核映射PKMAP_BASE当开启CONFIG_HIGHMEM后物理内存超出 lowmem 上限的部分需要临时映射进内核空间才能被访问。PKMAP_BASE的定义在 arch/arm/include/asm/highmem.h#define PKMAP_BASE (PAGE_OFFSET - PMD_SIZE) #define LAST_PKMAP PTRS_PER_PTE #define LAST_PKMAP_MASK (LAST_PKMAP - 1) #define PKMAP_NR(virt) (((virt) - PKMAP_BASE) PAGE_SHIFT) #define PKMAP_ADDR(nr) (PKMAP_BASE ((nr) PAGE_SHIFT))PKMAP_BASE紧贴在PAGE_OFFSET之下相距一个 PMD_SIZE与 arch/arm/include/asm/memory.h 中highmem pkmap 虚拟空间共享模块区末尾的注释相互印证开启 HIGHMEM 时MODULES_END被设为PAGE_OFFSET - PMD_SIZE即模块区与 pkmap 区在PAGE_OFFSET之下相接。同时arch/arm/include/asm/highmem.h 还处理了 VIVT 缓存下 kmap 的复杂性VIVT 缓存需要kmap_high_get()保证原子上下文中页的引用计数不归零并定义了arch_kmap_local_pre_unmap在解除映射前刷写 dcache。3.11 内核模块空间MODULES_VADDR ~ MODULES_ENDinsmod插入的内核模块通过动态映射放在MODULES_VADDR~MODULES_END之间。定义在 arch/arm/include/asm/memory.h#ifndef CONFIG_THUMB2_KERNEL #define MODULES_VADDR (PAGE_OFFSET - SZ_16M) #else /* smaller range for Thumb-2 symbols relocation (2^24)*/ #define MODULES_VADDR (PAGE_OFFSET - SZ_8M) #endif #ifdef CONFIG_HIGHMEM #define MODULES_END (PAGE_OFFSET - PMD_SIZE) #else #define MODULES_END (PAGE_OFFSET) #endif两个值得注意的细节头文件注释强调模块空间必须在TASK_SIZE与PAGE_OFFSET之间且离内核文本 32MB 以内——ARM 的BL分支指令/重定位范围有限模块必须放在内核映像附近才能被直接调用Thumb-2 内核使用更小的 8MB 窗口因为 Thumb-2 符号重定位范围只有2^24。3.12 KASan shadow memoryTASK_SIZE ~ MODULES_VADDR-1启用CONFIG_KASAN后MODULES_VADDR之下的整段空间被用作内核地址消毒器的影子内存。完整的布局图与公式定义在 arch/arm/include/asm/kasan_def.h#define KASAN_SHADOW_SCALE_SHIFT 3 #define KASAN_SHADOW_OFFSET _AC(CONFIG_KASAN_SHADOW_OFFSET, UL) #define KASAN_SHADOW_END ((UL(1) (32 - KASAN_SHADOW_SCALE_SHIFT)) \ KASAN_SHADOW_OFFSET) #define KASAN_SHADOW_START ((KASAN_SHADOW_END 3) KASAN_SHADOW_OFFSET)该文件给出的地址换算公式为shadow_addr (address 3) KASAN_SHADOW_OFFSET;即 shadow 内存中每个字节覆盖 8 字节内核内存对应原文档所说的每字节内存 1 比特影子比例。文档内的 ASCII 布局图清晰地展示了静态内核映像与页表PAGE_OFFSET之上、模块空间KASAN_SHADOW_END MODULES_VADDR之上、shadow 区KASAN_SHADOW_START~KASAN_SHADOW_END与用户空间0~TASK_SIZE的纵向关系KASAN_SHADOW_START正是TASK_SIZE见 arch/arm/include/asm/memory.h启用 KASan 时TASK_SIZE被定义为KASAN_SHADOW_START。CONFIG_KASAN_SHADOW_OFFSET由 Kconfig 根据 VMSPLIT 布局计算得出。3.13 用户空间00001000 ~ TASK_SIZE-1与 NULL 指针陷阱页00000000 ~ 00000fff00001000到TASK_SIZE-1是用户空间线程/进程的映射由mmap()系统调用放置。TASK_SIZE的定义同样在 arch/arm/include/asm/memory.h#ifndef CONFIG_KASAN #define TASK_SIZE (UL(CONFIG_PAGE_OFFSET) - UL(SZ_16M)) #else #define TASK_SIZE (KASAN_SHADOW_START) #endif #define TASK_UNMAPPED_BASE ALIGN(TASK_SIZE / 3, SZ_16M)未启用 KASan 时用户空间顶端比PAGE_OFFSET低 16MB这 16MB 正是模块空间的窗口。而TASK_UNMAPPED_BASE取TASK_SIZE / 3并对齐到 16MB作为mmap区域的起始下界。地址0x0~0xfff首 4KB 页是 CPU 向量页 / NULL 指针陷阱页不支持向量重映射的 CPU 把向量表放在这里内核与用户空间的 NULL 指针解引用由此映射捕获是CONFIG_DEFAULT_MMAP_MIN_ADDR与mmap_min_addr机制在架构层面的基础。相应地arch/arm/include/asm/pgtable.h 定义了用户空间允许映射的最低地址#define FIRST_USER_ADDRESS (PAGE_SIZE * 2)用户态第一个合法映射从第 2 个物理页开始从架构层面保证了 NULL 页不可被映射。四、平台开发者的红线冲突的后果原文档用一段严肃的警告收尾这也是平台移植工程师必须牢记的契约与上述区域冲突的映射可能导致内核无法启动non-bootable kernel或导致内核在运行时最终panic。同时考虑到未来 CPU 可能继续改变内核映射布局用户程序不得访问其0x00001000~TASK_SIZE范围之外任何未映射的内存如需访问这些区域必须通过open()与mmap()自行建立映射。这条规则既保护用户程序不受内核布局演进的影响也保证了内核保留区的完整性。五、阅读与验证指南布局总表见本文第二节完整继承自 Documentation/arch/arm/memory.rst关键常量源头PAGE_OFFSET/TASK_SIZE/MODULES_VADDR/MODULES_END/FDT_FIXED_BASE/VECTORS_BASE/ITCM_OFFSET/DTCM_OFFSET/ 物理-虚拟地址转换见 arch/arm/include/asm/memory.hvmalloc 区间VMALLOC_START/VMALLOC_END/VMALLOC_OFFSET见 arch/arm/include/asm/pgtable.hFixmap 区FIXADDR_START/FIXADDR_END与各固定槽位枚举见 arch/arm/include/asm/fixmap.hHIGHMEM pkmap 区PKMAP_BASE/LAST_PKMAP及 VIVT 缓存处理见 arch/arm/include/asm/highmem.hKASan shadow 布局与换算公式见 arch/arm/include/asm/kasan_def.hminicache 页拷贝实现SA11xx / XScale 的copy_user_highpage与L_PTE_MT_MINICACHE见 arch/arm/mm/copypage-xscale.c 及 arch/arm/include/asm/page.h 中的多 CPU 类型分发机器静态映射含 PCI I/O统一经iotable_init()进入 vmalloc 区调用实例见 arch/arm/mm/dma-mapping.c。掌握了这张地址地图你在阅读内核启动信息、调试设备驱动映射、排查地址越界导致系统崩溃类问题时就能快速判断某一虚拟地址落在哪个功能区并据此推断其行为语义——这正是 ARM 平台内核开发的底层基本功。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询