当前位置:首页>Linux>Linux DDR (内存) 初始化流程

Linux DDR (内存) 初始化流程

  • 2026-10-11 07:35:08
Linux DDR (内存) 初始化流程
音频讲解

概述

DDR 初始化分为两个主要阶段:

  1. 硬件初始化阶段
    (Bootloader/固件)- DRAM PHY 训练和初始化
  2. 软件初始化阶段
    (Linux 内核)- 内存管理系统初始化

第一阶段:DDR 硬件初始化 (Bootloader)

这是在操作系统启动之前由 Bootloader(如 U-Boot、UEFI)或硬件固件完成的初始化,主要用于物理层训练和配置。

1. 电源按序上电

  • 顺序
    : VDD1 → VDD2 → VDDQ
  • 目的
    : 相互隔离,保障高速低功耗运行

2. 复位稳定释放

  • RESET_n 拉低保持 tINIT1 ≥ 200us
  • 释放后等待 tINIT3 ≥ 2us
  • 目的
    : 强制 DRAM 进入初始化,清除所有内部寄存器和状态机

3. 时钟稳定启振 + CKE 激活

  • CK_t/CK_c 差分时钟稳定
  • 拉高 CKE 使能内部电路
  • 目的
    : 提供稳定的差分参考时钟

4. 写入 MR 模式寄存器

  • 在低速(≤50MHz)安全频率下配置
  • 目的
    : 配置 DRAM 基础工作模式

5. ZQ 阻抗校准

  • 发送 ZQCL 长校准命令
  • 目的
    : 保证高速信号完整性的基础

6. 命令/地址训练(CA 训练)

  • 目的
    : 校准 CA 总线与 CLK 的相位关系

7. 写入均衡训练(Write Leveling)

  • 目的
    : 使每个颗粒的 DQS 上升沿与 CLK 上升沿精确对齐,满足 tDQSS 时序要求

8. DQ/DBI 数据位训练

  • 读门控训练
  • 读写居中训练

9. 进入正常读写工作状态

  • 所有初始化和训练完成
  • DRAM 可响应正常的读写、刷新和电源管理命令

第二阶段:Linux 内核软件初始化

2.1 启动初始化 (start_kernel)

文件位置: init/main.c

start_kernel() {
    // ... 前期初始化 ...
    setup_arch(&command_line);  // 架构相关设置,包含内存检测
    mm_core_init();             // 内存管理核心初始化
    // ... 其他初始化 ...
}

2.2 架构相关初始化 (setup_arch)

此阶段主要由各个架构的代码实现,通过 zone_sizes_init() 函数完成:

  • 检测物理内存布局
  • 设置各 zone 的最大 PFN
  • 调用 free_area_init() 初始化内存管理数据结构

主要文件:

  • arch/<arch>/kernel/setup.c
  • arch/<arch>/mm/init.c

关键函数 zone_sizes_init():

void __init zone_sizes_init(void) {
unsignedlong max_zone_pfns[MAX_NR_ZONES];
// 设置各 zone 的最大 PFN
    max_zone_pfns[ZONE_DMA] = min(MAX_DMA_PFN, max_low_pfn);
    max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
// ...
    free_area_init(max_zone_pfns);  // 核心初始化
}

2.3 内存管理核心初始化 (free_area_init)

文件位置: mm/mm_init.c

这是内存初始化的核心阶段,由 free_area_init() 完成:

  1. free_area_init_node() - 初始化每个 NUMA 节点

    • 计算节点的总页数和各 zone 的范围
    • 分配并初始化 mem_map 数组
    • 设置 pgdat 内部结构
  2. memmap_init() - 初始化内存映射

    • 遍历所有内存区域,为每个物理页面初始化 struct page
    • 设置页面标志、引用计数、链表指针等

调用关系:

setup_arch()
  └─> zone_sizes_init()
        └─> free_area_init(max_zone_pfns)
              ├─> free_area_init_node()  // 初始化各节点
              └─> memmap_init()          // 初始化页面映射

2.4 内存管理完成初始化 (mm_core_init)

文件位置: mm/mm_init.c

在 start_kernel() 中调用,完成内存管理系统的最终初始化:

void __init mm_core_init(void) {
    build_all_zonelists(NULL);     // 构建 zonelist
    page_alloc_init_cpuhp();       // 初始化每CPU页面缓存
    page_ext_init_flatmem();       // 初始化页面扩展信息
    mem_init();                    // 释放 memblock,添加内存到伙伴系统
    mem_init_print_info();         // 打印内存信息
    kmem_cache_init();             // 初始化 slab 分配器
    vmalloc_init();                // 初始化 vmalloc 系统
// ...
}

关键函数 mem_init():

  • 释放 memblock 分配器占用的内存
  • 将所有可用页面添加到伙伴系统
  • 设置内存管理系统就绪标志

2.5 页面分配器后期初始化

文件位置: mm/page_alloc.c

关键函数:

  • page_alloc_init_late()
     - 处理延迟初始化的页面
  • 在 kernel_init_freeable() 中调用,此时调度器已就绪

2.5 核心数据结构

2.5.1 pg_data_t (节点数据)

每个 NUMA 节点对应一个 pg_data_t 结构体,包含:

  • node_zones[] - 该节点的内存区域数组
  • node_mem_map - 该节点的 page 结构体数组
  • 其他内存管理相关信息

2.5.2 zone (内存区域)

系统将内存划分为不同的区域:

  • ZONE_DMA - DMA 可用内存
  • ZONE_NORMAL - 常规内存
  • ZONE_HIGHMEM - 高端内存(32位系统)
  • ZONE_MOVABLE - 可移动页面区域
  • ZONE_DEVICE - 设备内存

每个 zone 包含:

  • free_area[] - 伙伴系统的空闲区域
  • managed_pages - 管理的页面数
  • zone_pgdat - 指向所属的 pg_data_t

2.5.3 struct page

表示一个物理页面的结构体,包含:

  • 页面标志
  • 引用计数
  • 链表指针
  • 映射信息

2.6 详细软件初始化流程

2.6.1 早期内存管理 (memblock)

系统启动初期使用 memblock 分配器管理内存:

memblock_add()        // 添加内存区域
memblock_reserve()    // 预留内存区域
memblock_alloc()      // 分配内存

2.6.2 节点初始化 (free_area_init_node)

对每个 NUMA 节点执行:

  1. 计算节点的总页数
  2. 初始化该节点的各个 zone
  3. 分配并初始化 mem_map 数组
  4. 设置 pgdat 内部结构

2.6.3 内存区域初始化 (free_area_init_core)

对每个 zone 执行:

  1. 计算 zone 的 spanned_pages 和 present_pages
  2. 初始化 zone 的空闲链表
  3. 设置 zone 的管理页面数
  4. 初始化每CPU页面缓存

2.6.4 页面结构初始化 (memmap_init)

为每个物理页面初始化 struct page:

void __init memmap_init_zone_range(struct zone *zone, ...) {
for (pfn = start_pfn; pfn < end_pfn; pfn++) {
structpage *page = pfn_to_page(pfn);
        __init_single_page(page, pfn, zone_idx, nid);
// 初始化页面标志、引用计数等
    }
}

2.6.5 内存初始化完成 (mem_init)

void __init mem_init(void) {
// 释放 memblock 分配器
    memblock_free_all();

// 将所有可用页面添加到伙伴系统
// 设置内存管理系统就绪标志
}

2.8 关键函数调用链

start_kernel()
  └─> setup_arch()
        └─> zone_sizes_init()
              └─> free_area_init(max_zone_pfns)
                    ├─> free_area_init_node()
                    │     ├─> calculate_node_totalpages()
                    │     ├─> alloc_node_mem_map()
                    │     └─> free_area_init_core()
                    │           ├─> zone_init_internals()
                    │           ├─> setup_usemap()
                    │           └─> init_currently_empty_zone()
                    └─> memmap_init()
  └─> mm_core_init()
        ├─> build_all_zonelists()
        ├─> page_alloc_init_cpuhp()
        ├─> mem_init()
        └─> kmem_cache_init()
  └─> kernel_init_freeable()
        └─> page_alloc_init_late()

2.9 初始化时机

阶段
函数
位置
功能
架构初始化
setup_arch → zone_sizes_init
arch/*/mm/init.c
检测内存布局,调用 free_area_init
内存核心
free_area_init
mm/mm_init.c
初始化 pg_data_t 和 zone 结构
内存核心
memmap_init
mm/mm_init.c
初始化 struct page 数组
内存完成
mm_core_init
mm/mm_init.c
完成内存管理系统初始化(含 mem_init)
延迟初始化
page_alloc_init_late
mm/page_alloc.c
初始化延迟页面(调度器就绪后)

相关文件

内核软件初始化

  • mm/mm_init.c - 内存初始化主文件
  • mm/page_alloc.c - 页面分配器实现
  • mm/memblock.c - 早期内存分配器
  • include/linux/mm.h - 内存管理头文件
  • include/linux/mmzone.h - 内存区域定义
  • arch/<arch>/mm/init.c
     - 架构相关内存初始化

DDR 控制器驱动

  • drivers/memory/emif.c - EMIF DDR 控制器驱动
  • drivers/memory/jedec_ddr_data.c - JEDEC DDR 时序参数

总结

DDR 初始化是一个从硬件到软件的完整过程:

  1. 硬件阶段
    :Bootloader 完成电源、时钟、复位、PHY 训练等物理层初始化
  2. 软件阶段
    :Linux 内核建立内存管理系统,从 memblock 早期分配器开始,逐步初始化节点、区域、页面结构,最终建立完整的伙伴系统

两个阶段紧密配合,才能让系统正常高效地使用 DDR 内存。

最新文章

随机文章