目录
1. 前言
本专题文章承接之前《kernel启动流程_head.S的执行》专题文章,我们知道在head.S执行过程中保存了bootloader传递的启动参数、启动模式以及FDT地址等,创建了内核空间的页表,最后为init进程初始化好了堆栈,并跳转到start_kernel执行。
本文重点介绍start_kernel的mm_init的主要流程.
kernel版本:5.10
平台:arm64
2. mm_init
|- -page_ext_init_flatmem
page_ext_init_flatmem();
在linux内核中支持3中内存模型,分别是:flat memory model,Discontiguous memory model和sparse memory model;
因为CONFIG_SPARSEMEM为true,因此采用sparse memory model,此函数为空
|- -init_debug_pagealloc
init_debug_pagealloc();
需要使能CONFIG_DEBUG_PAGEALLOC
|- -report_meminit
report_meminit();
打印结果如下:
[ 0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
|- -mem_init
mem_init();
mem_init
|--swiotlb_init(1)
|--set_max_mapnr(max_pfn - PHYS_PFN_OFFSET);
|--free_unused_memmap();
\--memblock_free_all()
|- - -swiotlb_init(1)
由于内存的物理空间较大,而DMA访问的地址空间较小,因此如果指定一个DMA无法访问的地址空间,就会通过一段提前分配的软件的buffer进行映射,让DMA可以访问这段内存空间,这样造成的结果是CPU与DMA看到的内存物理地址是不一样的,这个技术就是swiotlb
参考:https://blog.youkuaiyun.com/liuhangtiant/article/details/87825466
|- - -set_max_mapnr(max_pfn - PHYS_PFN_OFFSET);
设置全局max_mapnr为max_pfn - PHYS_PFN_OFFSET
其中,PHYS_PFN_OFFSET为内存物理首地址的页帧号,定义为:
#define PHYS_PFN_OFFSET (PHYS_OFFSET >> PAGE_SHIFT)
/* PHYS_OFFSET - the physical address of the start of memory. */
#define PHYS_OFFSET ({ VM_BUG_ON(memstart_addr & 1); memstart_addr; })
其中memstart_addr值为:0x40000000
arch/arm64/mm/init.c中有如下的赋值:
memstart_addr = round_down(memblock_start_of_DRAM()
max_pfn通过gdb打印看值为:0x80000
|- - -free_unused_memmap
遍历memblock.memory的所有range区域,释放没有使用的mem_map区域,实际就是这段区域对应的page
|- - -memblock_free_all
释放空闲页给buddy分配器
memblock_free_all
|--reset_all_zones_managed_pages
|--pages = free_low_memory_core_early()
|--for_each_reserved_mem_range(i, &start, &end)
| reserve_bootmem_region(start, end)
\--for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, &start, &end...)
count += __free_memory_core(start, end);
|- - - -reset_all_zones_managed_pages
遍历所有的node的所有zone,将zone的managed_pages初始化为0,atomic_long_set(&z->managed_pages, 0)

|- - - -free_low_memory_core_early
|--pages = free_low_memory_core_early()
|--for_each_reserved_mem_range(i, &start, &end)
| reserve_bootmem_region(start, end)
\--for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, &start, &end...)
count += __free_memory_core(start, end);
(1)遍历memblock.reserved的每个range,通过
reserve_bootmem_region->__SetPageReserved(page)来设置每个range对应的所有page为保留标志;
(2)遍历memblock.memory的每个range, 通过__free_memory_core将空闲range释放给buddy, 并统计释放页帧的数目
__free_memory_core(start, end)
|--__free_pages_memory

本文详细解析了Linux 5.10内核mm_init阶段的内存管理初始化流程,涉及内存模型、内存映射、DMA映射、slab机制、kmem_cache初始化等关键步骤,展示了内核启动时对内存资源的精细化管理。
最低0.47元/天 解锁文章
2025

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



