一、内核初始化中与内存管理相关的函数
目录
一、start_kernel中内存初始化相关函数梳理:
1、start_kernel下整体梳理
1.1、早期内存基础设施初始化
1.2、内存分配器核心初始化
1.3、内存相关后期初始化
2、重要部分梳理:setup_arch
2.1、阶段1:早期环境准备与内存探测
2.2、阶段2:关键内存区域保留
2.3、阶段3:memblock内存分配器初始化
2.4、阶段4:高级内存管理
3、重要部分梳理:mm_init
二、整体架构与重要函数
1、物理内存探测
2. 内存保留与分配器准备
3. NUMA与高级配置
4. 核心分配器设置
三、流程图梳理
参考文档:
一、start_kernel中内存初始化相关函数梳理:
整体C函数部分流程:
secondary_startup_64--> x86_64_start_kernel--> x86_64_start_reservations-> start_kernel
1、start_kernel下整体梳理
1.1、早期内存基础设施初始化
--> page_address_init //初始化页地址映射哈希表(用于highmem,x86_64通常为空操作)
--> setup_arch(&command_line) //架构相关内存初始化, e820/memblock初始化, 直接内存映射建立, NUMA/节点拓扑构建
--> setup_per_cpu_areas // 为每个CPU分配专属内存区域(减少多核竞争),静态定义per-CPU变量内存分配,动态per-CPU内存池初始化
1.2、内存分配器核心初始化
--> build_all_zonelists // 构建所有NUMA节点的zone分配顺序链表,采用本地节点优先策略
--> page_alloc_init // 伙伴系统(Buddy System)初始化,释放所有启动阶段预留的内存到伙伴系统,初始化每CPU页框缓存(pcp,Per-CPU Pageset)
--> mm_init // Linux 内核初始化过程中内存管理子系统(Memory Management)的核心入口
1.3、内存相关后期初始化
--> kmem_cache_init_late // SLAB分配器后期优化: 合并小缓存池, 初始化调试功能(如SLUB_DEBUG)
--> setup_per_cpu_pageset // 完善每CPU页框缓存(pcp)配置:调整高/低水位线, 激活冷热页分配策略
--> numa_policy_init // NUMA内存策略子系统初始化: 默认策略(MPOL_DEFAULT)设置, 绑定策略(MPOL_BIND)支持
--> mem_encrypt_init // 内存加密后期处理(AMD SEV/SME)
--> kfence_init // 内存错误检测工具初始化
--> kcsan_init // 内存竞争检测器初始化
2、重要部分梳理:setup_arch
setup_arch为体系结构相关的初始化代码。X64系统对应的setup_arch定义在arch/x86/kernel/setup.c,函数中与内存管理有关的调用:
2.1、阶段1:早期环境准备与内存探测
基础映射
--> early_ioremap_init() // 初始化临时I/O映射框架(基于fixmap)
--> setup_olpc_ofw_pgd() // OLPC特殊页表处理(X86特有)物理内存探测
--> e820__memory_setup // 解析BIOS提供的E820内存映射表,建立初始物理内存布局
--> e820__reserve_setup_data //将Boot Loader扩展的数据区标记为内核保留区域,并为其分配内存映射
--> parse_setup_data() // 解析ACPI/DTB等Bootloader数据
--> e820__finish_early_params //更新e820表。用户可以通过Loader传入内核CMD line来自定义内存区域映射
--> e820_add_kernel_range // 将内核_text 到 _end区域加入到e820表
--> e820__end_of_ram_pfn // 从e820获取最大物理页帧号
2.2、阶段2:关键内存区域保留
--> early_reserve_memory // 在memblock生效前保留关键内存区域
--> reserve_real_mode() // 强制保留低端1MB(实模式代码)
--> e820__reserve_setup_data() // 保留ACPI/DTB等数据区
--> e820__memblock_alloc_reserved_mpc_new() // 保留MP配置表
--> reserve_crashkernel() // 保留kexec崩溃内核区域
2.3、阶段3:memblock内存分配器初始化
--> early_alloc_pgt_buf // 为初始化过程中分配PGT预留堆空间
--> e820__memblock_setup // 将e820内存分布表的数据读出,并填写到Boot分配器管理
--> init_mem_mapping // 建立完整的直接内存映射(Direct Mapping)
--> cleanup_highmap() // 清除临时高端映射
2.4、阶段4:高级内存管理
--> initmem_init // 初始化NUMA(如果开启对应Kconfig的话),为memblock的现有区域分配NUMA节点ID号
--> x86_init.paging.pagetable_init //调用 native_pagetable_init 来初始化paging
--> sync_initial_page_table // 确保多核系统中页表的一致性,避免因 TLB 缓存不一致引发的内存访问错误
--> e820__reserve_resources // 为e820表项分配IO resource (reserve标记的表项除外)
--> x86_init.resources.reserve_resources //使用reserve_standard_io_resources 为下面硬件端口分配ioport resource
--> e820__setup_pci_gap // 在0到4GB找到空闲的内存区域,用于PCI设备的IO映射
至此,setup_arch内存初始化部分就完成了
3、重要部分梳理:mm_init
--> mm_init()--> page_ext_init_flatmem() // 初始化page_ext元数据系统(扩展struct page功能),分配连续物理页存储元数据数组, 建立pfn到page_ext的映射关系--> init_mem_debugging_and_hardening() // 内存调试与安全加固--> kfence_alloc_pool() // 分配KFENCE监控内存池,保留16MB专用内存区域--> report_meminit() // 打印物理内存布局信息:总内存/可用内存大小,内核代码/数据区位置--> stack_depot_init() // 初始化栈回溯存储系统,分配哈希表存储调用栈--> mem_init() // 物理内存最终移交--> free_all_bootmem() // 释放所有bootmem内存到伙伴系统--> page_ext_init_flatmem_late() // 延迟page_ext初始化:激活page_owner追踪,注册内存热插拔回调--> kmem_cache_init() // SLAB分配器初始化:创建静态缓存(kmem_cache, kmalloc_caches),替换临时分配器接口--> kmemleak_init() // 动态内存泄漏检测:创建扫描线程, 注册kmalloc/vmalloc钩子--> pgtable_init() // 页表缓存初始化--> x86: 初始化pgd/pmd/pte缓存,ARM: 配置硬件页表格式--> debug_objects_mem_init() // 调试对象内存分配:创建obj_hash缓存,初始化静态对象池--> vmalloc_init() // 非连续内存区初始化:划分vmalloc地址空间,初始化红黑树管理结构--> pti_init() [x86] // 页表隔离初始化,分离用户/内核页表,配置CR3切换机制
二、整体架构与重要函数
整体数据流向:BIOS E820 → memblock → 伙伴系统 → SLAB/kmalloc,其中:
- memblock:启动期间临时管理物理内存
- 伙伴系统:按阶管理物理页,解决外部碎片
- SLAB:对象级缓存,解决内部碎片
其中重要的API入口:
1、物理内存探测
功能说明 | |
---|---|
e820__memory_setup() | 解析BIOS E820内存映射表,生成e820_table(区分RAM/Reserved类型) |
e820__finish_early_params() | 处理cmdline内存参数(如memmap=),动态修改内存布局 |
mtrr_bp_init() | 配置MTRR(Memory Type Range Registers),标记UC/WC等特殊内存区域 |
2. 内存保留与分配器准备
技术细节 | |
---|---|
early_reserve_memory() | 保留内核代码/数据区、ACPI表等关键区域到memblock.reserved |
reserve_real_mode() | 强制保留低端1MB内存(实模式代码、AP启动区域) |
e820__memblock_setup() | 转换E820表到memblock分配器,建立memblock.memory和.reserved |
init_mem_mapping() | 建立直接物理映射(__START_KERNEL_map偏移),支持5级页表(LA57) |
3. NUMA与高级配置
函数 | 作用 |
---|---|
initmem_init() | 解析SRAT表构建NUMA拓扑,为内存块分配节点ID(nid) |
e820__setup_pci_gap() | 在0-4GB范围寻找PCI设备MMIO空洞,避免内存冲突 |
sync_initial_page_table() | 同步多核TLB,确保内存映射一致性 |
4. 核心分配器设置
阶段 | 操作 |
---|---|
伙伴系统 | build_all_zonelists()构建NUMA zone分配顺序,page_alloc_init()启用每CPU缓存 |
SLAB分配器 | kmem_cache_init()创建通用缓存(kmalloc-*),kmem_cache_init_late()优化合并 |
非连续内存 | vmalloc_init()划分VMALLOC区域,红黑树管理虚拟地址空洞 |
三、流程图梳理
参考文档:
Linux内核内存管理 - 初始化C代码中的内存处理概览 | L&H SITE
【计算子系统】内存管理之一:地址映射
linux内存初始化初期内存分配器——memblock-阿里云开发者社区
(3 封私信 / 79 条消息) linux内存管理(一)内存初始化 - 知乎