#Linux内存管理# 假设设备上安装了一块2G的物理内存,在系统启动时,ARM Linux内核是如何映射的?

发布于:2025-04-07 ⋅ 阅读:(22) ⋅ 点赞:(0)

在ARM Linux系统启动过程中,对2GB物理内存的映射实现分为以下几个关键阶段:

 

一、设备树解析与内存信息获取

 

1.设备树定义

物理内存范围通过设备树(DTS)的memory节点定义,例如:

   memory@60000000 {

       device_type = "memory";

       reg = <0x60000000 0x80000000>; // 起始地址0x60000000,大小2GB

   };

作用:内核启动时通过early_init_dt_scan_memory()解析该节点,获取物理内存基地址(base)和大小(size)。

 

2.注册到memblock子系统

解析后的内存区域通过memblock_add()注册到内核早期内存管理器memblock中,临时管理物理内存分配。

 

二、初始页表建立(段映射阶段)

1.临时页表(swapper_pg_dir)

 

位置:物理内存16KB对齐区域,由汇编代码在head.S中初始化。

 

映射方式:采用段映射(Section Mapping),每个段1MB,直接映射物理地址到内核虚拟地址。

 

     // 物理地址0x60000000 → 虚拟地址0xC0000000(PAGE_OFFSET=3G)

     create_section_mapping(swapper_pg_dir, 0x60000000, 0xC0000000, 0x80000000);

  

2.映射范围

 

初始页表覆盖整个2GB物理内存,建立1:1线性映射(物理地址+3G偏移),确保内核启动时可直接访问所有内存。

 

确定映射偏移量(PAGE_OFFSET)

典型配置:内核虚拟地址偏移 PAGE_OFFSET = 0xC0000000。

物理地址 0x60000000 对应的内核虚拟地址为:

虚拟地址 = 物理地址 + (PAGE_OFFSET - PHYS_OFFSET)

其中 PHYS_OFFSET 是物理内存的起始地址(此处为 0x60000000)。

 

计算示例:

PAGE_OFFSET = 0xC0000000 // 内核空间起始地址

PHYS_OFFSET = 0x60000000 // 物理内存起始地址

虚拟地址偏移量 = PAGE_OFFSET - PHYS_OFFSET = 0xC0000000 - 0x60000000 = 0x60000000

 

因此,物理地址 0x60000000 对应的内核虚拟地址为:

0x60000000 + 0x60000000 = 0xC0000000

物理地址 0xE0000000(结束地址)对应的虚拟地址为:

0xE0000000 + 0x60000000 = 0x140000000(超过 32 位地址空间,需特殊处理)。

 

虚拟地址空间布局

内核空间:0xC0000000-0xFFFFFFFF(1GB,默认配置)。

若物理内存超过 1GB(如本例的 2GB),需调整 PAGE_OFFSET 或启用 HIGHMEM,但 ARMv7 通常不推荐此操作。

 

冲突处理:

当 PHYS_OFFSET + 内存大小 > PAGE_OFFSET + 1GB 时,内核会触发错误或截断映射。需确保设备树中 reg 范围与内核配置一致。

 

三、详细页表重构(paging_init阶段)

1.清除临时映射

 

在paging_init()函数中,调用memblock_free()释放临时页表占用的内存。

 

2.重构低端内存映射

 

函数路径:paging_init() → map_lowmem() → create_mapping()。

 

映射策略:

 

使用2MB大页(Huge Page)或4KB小页,替代初始的1MB段映射。

 

保留线性映射关系(phys_to_virt(addr) = addr + PAGE_OFFSET)。

 

3.高端内存处理(如适用)

若物理内存超过内核直接映射区(如32位系统默认896MB),超出部分标记为高端内存(Highmem),通过kmap()动态映射。

 

2GB内存案例:在32位ARM系统(内核空间1G)中,若配置为3:1划分,2GB物理内存需部分使用高端内存机制。

 

四、内存管理子系统初始化

1.伙伴系统接管

 

memblock将内存信息移交伙伴系统(Buddy System),完成精细化分页管理:

   start_kernel() → mm_init() → mem_init() → free_all_bootmem();

 

2.内存区域(Zone)划分

 

ZONE_NORMAL:直接映射的低端内存(如0x60000000-0xE0000000)。

 

ZONE_HIGHMEM:动态映射的高端内存(若存在)。

 

五、验证与调试

查看内核日志

   dmesg | grep "Memory" # 输出示例:

   # [0.000000] Memory: 2048MB/2048MB available

   

检查物理内存映射

   cat /proc/iomem | grep "System RAM"

   # 输出示例:

   # 60000000-7fffffff : System RAM

 

关键代码路径与配置文件阶段

关键函数/文件 作用

设备树解析 early_init_dt_scan_memory() 解析DTS内存节点

初始页表构建 arch/arm/kernel/head.S 创建临时段映射

详细页表重构 arch/arm/mm/mmu.c → map_lowmem() 建立低端内存永久映射

内存管理移交 mm/memblock.c → memblock_free() 释放临时页表,移交伙伴系统

 

实际映射策略可能因内核配置(如CONFIG_VMSPLIT调整用户/内核空间比例)或芯片架构差异而不同。