memory:表示可用可分配的内存;
结束完memblock算法初始化前的准备工作,回到memblock算法初始化及其算法实现上面。memblock是一个很简单的算法。
memblock算法的实现是,它将所有状态都保存在一个全局变量__initdata_memblock中,算法的初始化以及内存的申请释放都是在将内存块的状态做变更。那么从数据结构入手,
__initdata_memblock是一个memblock结构体。其结构体定义:
【file:/include/linux/memblock.h】
struct memblock {
bool bottom_up; /* is bottom up direction? */
phys_addr_t current_limit;
struct memblock_type memory;
struct memblock_type reserved;
};
结构体内各成员的意思:
- bottom_up:用来表示分配器分配内存是自低地址(低地址指的是内核映像尾部,下同)向高地址还是自高地址向低地址来分配的;
- current_limit:用来表示用来限制memblock_alloc()和memblock_alloc_base(..., MEMBLOCK_ALLOC_ACCESSIBLE)的内存申请;
- memory:表示可用可分配的内存;
- reserved:表示已经分配出去了的内存;
memory和reserved是很关键的一个数据结构,memblock算法的内存初始化和申请释放都是围绕着它们转。
往下看看memory和reserved的结构体memblock_type定义:
【file:/include/linux/memblock.h】
struct memblock_type {
unsigned long cnt; /* number of regions */
unsigned long max; /* size of the allocated array */
phys_addr_t total_size; /* size of all regions */
struct memblock_region *regions;
};
cnt和max分别表示当前状态(memory/reserved)的内存块可用数和可支持的最大数,total_size则表示当前状态(memory/reserved)的空间大小(也就是可用的内存块信息大小总和),而regions则是用于保存内存块信息的结构(包括基址、大小和标记等):
【file:/include/linux/memblock.h】
struct memblock_region {
phys_addr_t base;
phys_addr_t size;
unsigned long flags;
#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
int nid;
#endif
};
memblock算法的主要结构体也就这么多了,总的关系如图:
回去看看__initdata_memblock的定义:
【file:/mm/memblock.c】
static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_memblock;
static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_memblock;
struct memblock memblock __initdata_memblock = {
.memory.regions = memblock_memory_init_regions,
.memory.cnt = 1, /* empty dummy entry */
.memory.max = INIT_MEMBLOCK_REGIONS,
.reserved.regions = memblock_reserved_init_regions,
.reserved.cnt = 1, /* empty dummy entry */
.reserved.max = INIT_MEMBLOCK_REGIONS,
.bottom_up = false,
.current_limit = MEMBLOCK_ALLOC_ANYWHERE,
};
它初始化了部分成员,表示内存申请自高地址向低地址,且current_limit设为~0,即0xFFFFFFFF,同时通过全局变量定义为memblock的算法管理中的memory和reserved准备了内存空间。
接下来分析一下memblock算法初始化,其初始化函数为memblock_x86_fill(),初始化调用栈位置:
start_kernel() #/init/main.c
└->setup_arch() #/arch/x86/kernel/setup.c
└->memblock_x86_fill() #/arch/x86/kernel/e820.c
函数实现:
【file:/arch/x86/kernel/e820.c】
void __init memblock_x86_fill(void)
{
int i;
u64 end;
/*
* EFI may have more than 128 entries
* We are safe to enable resizing, beause memblock_x86_fill()
* is rather later for x86
*/
memblock_allow_resize();
for (i = 0; i < e820.nr_map; i++) {
struct e820entry *ei = &e820.map[i];
end = ei->addr + ei->size;
if (end != (resource_size_t)end)
continue;
if (ei->type != E820_RAM && ei->type != E820_RESERVED_KERN)
continue;
memblock_add(ei->addr, ei->size);
}
/* throw away partial pages */
memblock_trim_memory(PAGE_SIZE);
memblock_dump_all();
}
该函数的实现中,调用了memblock_allow_resize() 仅是用于置memblock_can_resize的值;里面的for则是用于循环遍历e820的内存布局信息,将信息做memblock_add的操作;最后循环退出后,将调用memblock_trim_memory()和memblock_dump_all()做后处理。这里首先看一下memblock_add()的函数实现:
【file:/mm/memblock.c】
int __init_memblock memblock_add(phys_addr_t base, phys_addr_t size)
{
return memblock_ad