Linux内核使用伙伴系统管理内存,那么在伙伴系统工作前,如何管理内存?答案是memblock;
memblock在系统启动阶段进行简单的内存管理,记录物理内存的使用情况;
在进一步介绍memblock之前,有必要先了解下系统内存的使用情况:
首先,内存中的某些部分是永久的分配给内核的,比如内核代码段和数据段,ramdisk和fdt占用的空间等,它们是系统内存的一部分,但是不能被侵占,也不参与内存分配,称之为静态内存;
其次,GPU,Camera等都需要预留大量连续内存,这部分内存平时不用,但是系统必须提前预留好,称之为预留内存;
最后,内存的其余部分称之为动态内存,是需要内核管理的宝贵资源;
memblock把物理内存划分为若干内存区,按使用类型分别放在memory和reserved两个集合(数组)中,memory即动态内存的集合,reserved集合包括静态内存和预留内存;
1. memblock关键数据结构
memblock数据结构定义如下:
struct memblock {
bool bottom_up;
phys_addr_t current_limit;
struct memblock_type memory;
struct memblock_type reserved;
};
bool bottom_up;
phys_addr_t current_limit;
struct memblock_type memory;
struct memblock_type reserved;
};
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;
};
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;
};
struct memblock_region {
phys_addr_t base;
phys_addr_t size;
unsigned long flags;
};
phys_addr_t base;
phys_addr_t size;
unsigned long flags;
};
memblock相关数据结构十分的简单,内核还为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,
};
.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,
};
memory类型的内存集合指向memblock_memory_init_regions数组,最多可以记录128个内存区;
reserved类型的内存集合指向memblock_reserved_init_regions数组,最多可以记录128个内存区;
注:内核代码经常用到类似"__initdata_memblock"的宏定义,通常用来指定变量或函数所在的section,该宏的定义如下:
#define __meminitdata __attribute__ ((__section__(".meminit.data")))
2. memblock基本操作
1) 添加内存区
int
memblock_add
(phys_addr_t
base
, phys_addr_t
size
);
int
memblock_reserve
(phys_addr_t
base
, phys_addr_t
size
);
分别为memory和reserved集合添加内存区,如果新加入的内存区与原有内存区重叠,则合并到原有内存区,否则插入新内存区;
实际工作由memblock_add_range()完成,type参数指定内存集合类型;
需要注意的是该函数内部会执行两次:
第一次计算需要插入几个内存区,如果超过允许的最大内存区个数,则double内存区数组;
第二次执行内存区的实际插入与合并操作;
第二次执行内存区的实际插入与合并操作;
int
memblock_add_range
(struct memblock_type *
type
,
phys_addr_t base , phys_addr_t size ,
int nid, unsigned long flags)
{
phys_addr_t base , phys_addr_t size ,
int nid, unsigned long flags)
{
bool insert = false;
... ...
/* 特例: 如果内存集合为空,则不需要执行插入或合并操作,直接插入新的内存区就可以了 */
if (type->regions[0].size == 0) {
WARN_ON(type->cnt != 1 || type->total_size);
type->regions[0].base = base;
type->regions[0].size = size;
type->regions[0].flags = flags;
memblock_set_region_node(&type->regions[0], nid);
type->total_size = size;
return 0;
}
... ...
/* 特例: 如果内存集合为空,则不需要执行插入或合并操作,直接插入新的内存区就可以了 */
if (type->regions[0].size == 0) {
WARN_ON(type->cnt != 1 || type->total_size);
type->regions[0].base = base;
type->regions[0].size = size;
type->regions[0].flags = flags;
memblock_set_region_node(&type->regions[0], nid);
type->total_size = size;
return 0;
}
repeat:
base = obase;
nr_new = 0;
for (i = 0; i < type->cnt; i++) {
struct memblock_region *rgn = &type->regions[i];
phys_addr_t rbase = rgn->base;
phys_addr_t rend = rbase + rgn->size;
if (rbase >= end)
break;
if (rend <= base)
continue;
<
nr_new = 0;
for (i = 0; i < type->cnt; i++) {
struct memblock_region *rgn = &type->regions[i];
phys_addr_t rbase = rgn->base;
phys_addr_t rend = rbase + rgn->size;
if (rbase >= end)
break;
if (rend <= base)
continue;