Linux 内核中的**内存管理模块(Memory Management, MM)**从初始化到可供其他模块使用,需要经历一系列严格的阶段,涵盖硬件初始化、数据结构构建、接口暴露等过程。以下是关键阶段的详细说明:
---
### **1. 硬件相关初始化(Architecture-Specific Setup)**
#### **阶段目标**:检测物理内存布局,建立最基本的页表映射。
- **执行时机**:内核启动早期(`start_kernel()` 之前,由汇编代码处理)。
- **关键步骤**:
1. **探测物理内存**
- BIOS/UEFI 或设备树(DT)传递内存信息(如 `mem=` 内核参数)。
- 记录内存区域(`memblock` 或 `e820` 映射,位于 `arch/x86/kernel/e820.c`)。
2. **初始化页表**
- 建立临时内核页表(`arch/x86/mm/init_64.c` 的 `startup_64()`)。
- 启用分页机制(`cr3` 寄存器加载)。
3. **保留关键内存区域**
- 保留内核代码、数据段、启动参数等(`memblock_reserve()`)。
#### **边界**:
- 依赖体系结构代码(如 `arch/x86`, `arch/arm64`)。
- 不依赖其他内核模块(此时调度器、驱动等尚未初始化)。
---
### **2. 通用内存管理子系统初始化(`start_kernel()` 阶段)**
#### **阶段目标**:构建核心内存管理数据结构,替换临时分配器。
- **执行时机**:`start_kernel()` → `mm_init()`。
- **关键步骤**:
1. **初始化 `memblock` 分配器**
- 临时管理物理内存(`mm/memblock.c`),后续被伙伴系统取代。
2. **建立伙伴系统(Buddy System)**
- 解析 `memblock` 内存区域,初始化 `struct zone`(`mm/page_alloc.c` 的 `free_area_init()`)。
- 划分物理页帧(`struct page` 数组),管理空闲列表。
3. **初始化 SLAB/SLUB 分配器**
- 为内核对象(如 `task_struct`)创建缓存(`mm/slub.c` 的 `kmem_cache_init()`)。
4. **设置内存回收机制**
- 启动 `kswapd` 内核线程(`mm/vmscan.c`)。
5. **初始化虚拟内存(VM)**
- 建立 `vmalloc` 区域(`mm/vmalloc.c` 的 `vmalloc_init()`)。
#### **边界**:
- 依赖体系结构代码提供的物理内存信息。
- 不依赖动态模块(此时模块子系统未初始化)。
---
### **3. 动态内存接口暴露(API Ready)**
#### **阶段目标**:提供其他模块可调用的内存分配接口。
- **执行时机**:`mm_init()` 完成后,其他子系统开始初始化。
- **关键接口**:
1. **物理页分配**
- `alloc_pages()` / `__get_free_pages()`(`include/linux/gfp.h`)。
2. **SLAB 分配器**
- `kmalloc()` / `kfree()`(`include/linux/slab.h`)。
3. **虚拟内存映射**
- `vmalloc()` / `vfree()`(`mm/vmalloc.c`)。
4. **DMA 内存接口**
- `dma_alloc_coherent()`(`include/linux/dma-mapping.h`)。
5. **用户空间内存交互**
- `copy_to_user()` / `get_user_pages()`(`mm/memory.c`)。
#### **边界**:
- 通过 `EXPORT_SYMBOL()` 导出符号供其他模块使用。
- 依赖核心模块(如 `spinlock` 用于保护内存结构)。
---
### **4. 与其他模块的协作(Integration)**
#### **阶段目标**:响应其他模块的内存需求。
- **示例场景**:
1. **进程管理模块**
- 创建进程时调用 `mm_init()` 初始化 `mm_struct`(`kernel/fork.c`)。
2. **文件系统模块**
- 文件映射(`mmap()`)触发 `filemap_fault()` 加载页面缓存。
3. **设备驱动模块**
- 驱动通过 `kmalloc()` 分配内存,或 `ioremap()` 映射设备内存。
4. **网络模块**
- SKB 缓冲区分配(`alloc_skb()` 内部调用 `kmalloc()`)。
#### **边界**:
- 严格通过头文件中的 API 交互,避免直接访问内部数据结构。
---
### **5. 动态内存扩展(可选)**
#### **阶段目标**:支持内存热插拔、CMA(连续内存分配器)等高级功能。
- **执行时机**:运行时动态加载。
- **关键步骤**:
1. **内存热插拔**
- 通过 `add_memory()` / `remove_memory()` 动态增删内存(`drivers/base/memory.c`)。
2. **CMA 初始化**
- 为设备预留连续内存(`mm/cma.c` 的 `cma_init_reserved_areas()`)。
#### **边界**:
- 依赖设备驱动模块(如 NUMA 架构)。
---
### **关键函数调用链(示例)**
```text
start_kernel()
→ setup_arch() # 架构相关内存初始化
→ mm_init() # 核心内存管理初始化
→ mem_init() # 释放memblock到伙伴系统
→ kmem_cache_init() # SLAB初始化
→ vmalloc_init() # 虚拟内存区域初始化
→ rest_init() # 启动kswapd线程
```
---
### **总结:从无到有的阶段**
| **阶段** | **关键操作** | **依赖关系** |
|-------------------------|---------------------------------------|---------------------------|
| 硬件初始化 | 探测内存、建立页表 | 仅依赖体系结构代码 |
| 核心内存子系统初始化 | 伙伴系统、SLAB、VM初始化 | 依赖硬件初始化完成 |
| 接口暴露 | 导出 `kmalloc`、`alloc_pages` 等符号 | 依赖核心内存子系统 |
| 模块协作 | 为进程、文件系统、驱动提供内存服务 | 所有基础接口已就绪 |
| 动态扩展 | 内存热插拔、CMA | 可选,依赖具体需求 |
内存模块的初始化是内核启动的关键路径之一,必须按严格顺序执行,确保其他模块在需要内存时,底层机制已完全就绪。

被折叠的 条评论
为什么被折叠?



