Linux内核内存管理初探:Memblock机制详解
linux-insides-zh Linux 内核揭秘 项目地址: https://gitcode.com/gh_mirrors/lin/linux-insides-zh
前言
内存管理是操作系统内核中最复杂且关键的子系统之一。在Linux内核启动初期,当通用内存分配器尚未就绪时,内核需要一种机制来管理物理内存。本文将深入探讨Linux内核早期使用的内存管理机制——Memblock(内存块)。
内存管理概述
在Linux内核启动过程中,start_kernel
函数负责初始化所有内核特性。在此之前,内核已经建立了初期页表、识别页表和固定映射页表,但完整的内存管理系统尚未启动。Memblock机制正是在这个过渡阶段发挥重要作用。
Memblock基础概念
Memblock最初被称为"逻辑内存块",后经Yinghai Lu的补丁改进并更名为现在的名称。它是x86_64架构上内核早期使用的内存管理方法。
核心数据结构
Memblock系统主要由三个关键数据结构组成:
- memblock:顶层结构,包含整个内存块的全局信息
- memblock_type:描述特定类型的内存区域集合
- memblock_region:描述单个内存区域
1. memblock结构
struct memblock {
bool bottom_up; // 内存分配方向标志
phys_addr_t current_limit; // 当前内存限制
struct memblock_type memory; // 可用内存区域
struct memblock_type reserved; // 保留内存区域
#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP
struct memblock_type physmem; // 物理内存映射
#endif
};
2. memblock_type结构
struct memblock_type {
unsigned long cnt; // 当前区域数量
unsigned long max; // 最大区域容量
phys_addr_t total_size; // 所有区域总大小
struct memblock_region *regions; // 区域数组指针
};
3. memblock_region结构
struct memblock_region {
phys_addr_t base; // 区域基地址
phys_addr_t size; // 区域大小
unsigned long flags; // 区域标志
#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
int nid; // NUMA节点ID
#endif
};
Memblock初始化过程
Memblock的初始化在内核启动早期完成。全局的memblock实例定义如下:
struct memblock memblock __initdata_memblock = {
.memory.regions = memblock_memory_init_regions,
.memory.cnt = 1,
.memory.max = INIT_MEMBLOCK_REGIONS,
.reserved.regions = memblock_reserved_init_regions,
.reserved.cnt = 1,
.reserved.max = INIT_MEMBLOCK_REGIONS,
#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP
.physmem.regions = memblock_physmem_init_regions,
.physmem.cnt = 1,
.physmem.max = INIT_PHYSMEM_REGIONS,
#endif
.bottom_up = false,
.current_limit = MEMBLOCK_ALLOC_ANYWHERE,
};
关键点说明:
__initdata_memblock
宏控制memblock数据在内核初始化后的处理方式- 初始内存区域数组大小为128(由
INIT_MEMBLOCK_REGIONS
定义) - 默认内存分配方向为自顶向下
- 初始内存限制设置为最大可能值(
MEMBLOCK_ALLOC_ANYWHERE
)
Memblock核心API
Memblock提供了一系列API来管理内存区域,主要包括:
1. 添加内存区域
memblock_add
int memblock_add(phys_addr_t base, phys_addr_t size)
{
return memblock_add_range(&memblock.memory, base, size,
MAX_NUMNODES, 0);
}
该函数将新的可用内存区域添加到memblock中,实际工作由memblock_add_range
完成。
memblock_add_range
这是内存添加的核心函数,主要流程:
- 计算区域结束地址(考虑地址溢出)
- 检查与现有区域的重叠情况
- 处理不重叠部分
- 合并相邻区域
关键操作包括:
- 使用
memblock_cap_size
确保地址不溢出 - 通过
memblock_double_array
扩展区域数组(如果需要) - 使用
memblock_insert_region
插入新区域 - 调用
memblock_merge_regions
合并相邻区域
2. 保留内存区域
memblock_reserve
int memblock_reserve(phys_addr_t base, phys_addr_t size)
{
return memblock_add_range(&memblock.reserved, base, size,
MAX_NUMNODES, 0);
}
与memblock_add
类似,但操作的是保留内存区域。
3. 其他重要API
memblock_remove
:从内存块中移除内存区域memblock_find_in_range
:在指定范围内查找可用区域memblock_free
:释放内存块中的区域for_each_mem_range
:遍历内存区域
内存区域信息获取
Memblock提供了获取已分配内存信息的API:
phys_addr_t __init_memblock get_allocated_memblock_memory_regions_info(
phys_addr_t *addr)
{
if (memblock.memory.regions == memblock_memory_init_regions)
return 0;
*addr = __pa(memblock.memory.regions);
return PAGE_ALIGN(sizeof(struct memblock_region) *
memblock.memory.max);
}
类似地,get_allocated_memblock_reserved_regions_info
可用于获取保留区域信息。
调试支持
Memblock提供了多种调试手段:
1. 内核日志输出
通过在内核命令行添加memblock=debug
参数,可以启用详细日志输出:
#define memblock_dbg(fmt, ...) \
if (memblock_debug) printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
2. Debugfs接口
在支持debugfs的系统上,可以通过以下路径查看memblock状态:
/sys/kernel/debug/memblock/memory
/sys/kernel/debug/memblock/reserved
/sys/kernel/debug/memblock/physmem
实际应用示例
在内核初始化过程中,memblock_x86_fill
函数使用e820提供的内存映射信息,通过memblock_add
将内核保留的内存区域添加到memblock中。
总结
Memblock机制作为Linux内核早期内存管理的关键组件,为内核启动阶段提供了基本但可靠的内存管理能力。通过理解Memblock的数据结构和API,我们可以更深入地掌握Linux内存管理系统的初始化和工作流程。随着内核初始化的进行,Memblock最终会被更复杂的内存管理系统所取代,但其在启动阶段的作用不可替代。
linux-insides-zh Linux 内核揭秘 项目地址: https://gitcode.com/gh_mirrors/lin/linux-insides-zh
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考