转载地址:https://blog.youkuaiyun.com/u010383937/article/details/78596635
bootmem机制是在linux启动期间,buddy管理器,slab管理器没有初始化好时使用的内存管理方法。
之后系统起来之后,就交由buddy等方法来管理了。
在多核系统中,每个核都对应这个一个node_data结构体,其中就记录着这个核使用的内存,也包括了bootmem的信息。
看一下node_data的结构体
-
#define LEVELS_PER_SLICE 128
-
-
struct slice_data {
-
unsigned
long irq_enable_mask[
2];
-
int level_to_irq[LEVELS_PER_SLICE];
-
};
-
-
struct hub_data {
-
cpumask_t h_cpus;
-
unsigned
long slice_map;
-
unsigned
long irq_alloc_mask[
2];
-
struct slice_data slice[2];
-
};
-
-
struct node_data {
-
struct pglist_data pglist;
//记录内存信息
-
struct hub_data hub;
-
cpumask_t cpumask;
-
};
-
-
#define NODE_DATA(n) (&__node_data[(n)]->pglist)
-
#define hub_data(n) (&__node_data[(n)]->hub)
-
struct bootmem_data;
-
typedef
struct pglist_data {
-
struct zone node_zones[MAX_NR_ZONES];
//zone信息
-
struct zonelist node_zonelists[MAX_ZONELISTS];
//zone链表
-
int nr_zones;
-
#ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */
-
struct page *node_mem_map;
-
#ifdef CONFIG_MEMCG
-
struct page_cgroup *node_page_cgroup;
-
#endif
-
#endif
-
#ifndef CONFIG_NO_BOOTMEM
-
struct bootmem_data *bdata;
//bootmem信息
-
#endif
-
#ifdef CONFIG_MEMORY_HOTPLUG
-
spinlock_t node_size_lock;
-
#endif
-
unsigned
long node_start_pfn;
//物理地址开始
-
unsigned
long node_present_pages;
/*总的物理页数*/
-
unsigned
long node_spanned_pages;
/*总的物理大小,包括hole*/
-
int node_id;
//node号
-
nodemask_t reclaim_nodes;
/* Nodes allowed to reclaim from */
-
wait_queue_head_t kswapd_wait;
-
wait_queue_head_t pfmemalloc_wait;
-
struct task_struct *kswapd;
/* Protected by lock_memory_hotplug() */
-
int kswapd_max_order;
-
enum zone_type classzone_idx;
-
#ifdef CONFIG_NUMA_BALANCING
-
/* Lock serializing the migrate rate limiting window */
-
spinlock_t numabalancing_migrate_lock;
-
-
/* Rate limiting time interval */
-
unsigned
long numabalancing_migrate_next_window;
-
-
/*Number of pages migrated during the rate limiting time interval */
-
unsigned
long numabalancing_migrate_nr_pages;
-
#endif
-
}
pg_data_t;
-
typedef
struct bootmem_data {
-
unsigned
long node_min_pfn;
//地址开始
-
unsigned
long node_low_pfn;
//地址结束
-
void *node_bootmem_map;
// 位图信息存储位置
-
unsigned
long last_end_off;
//分配的最后一个页内的偏移
-
unsigned
long hint_idx;
//
-
struct list_head list;
//用于链表
-
}
bootmem_data_t;
-
//每个cpu都对应一个bootmem
-
bootmem_data_t bootmem_node_data[MAX_NUMNODES] __initdata;
-
-
//为每个cpu定义的node_data
-
struct node_data *__node_data[MAX_NUMNODES];
-
EXPORT_SYMBOL(__node_data);
bootmem的操作方法:
-
bootmem的操作函数:
-
#define alloc_bootmem(x) \
-
__alloc_bootmem(x, SMP_CACHE_BYTES, BOOTMEM_LOW_LIMIT)
-
#define alloc_bootmem_align(x, align) \
-
__alloc_bootmem(x, align, BOOTMEM_LOW_LIMIT)
-
#define alloc_bootmem_nopanic(x) \
-
__alloc_bootmem_nopanic(x, SMP_CACHE_BYTES, BOOTMEM_LOW_LIMIT)
-
#define alloc_bootmem_pages(x) \
-
__alloc_bootmem(x, PAGE_SIZE, BOOTMEM_LOW_LIMIT)
-
#define alloc_bootmem_pages_nopanic(x) \
-
__alloc_bootmem_nopanic(x, PAGE_SIZE, BOOTMEM_LOW_LIMIT)
-
#define alloc_bootmem_node(pgdat, x) \
-
__alloc_bootmem_node(pgdat, x, SMP_CACHE_BYTES, BOOTMEM_LOW_LIMIT)
-
#define alloc_bootmem_node_nopanic(pgdat, x) \
-
__alloc_bootmem_node_nopanic(pgdat, x, SMP_CACHE_BYTES, BOOTMEM_LOW_LIMIT)
-
#define alloc_bootmem_pages_node(pgdat, x) \
-
__alloc_bootmem_node(pgdat, x, PAGE_SIZE, BOOTMEM_LOW_LIMIT)
-
#define alloc_bootmem_pages_node_nopanic(pgdat, x) \
-
__alloc_bootmem_node_nopanic(pgdat, x, PAGE_SIZE, BOOTMEM_LOW_LIMIT)
-
-
#define alloc_bootmem_low(x) \
-
__alloc_bootmem_low(x, SMP_CACHE_BYTES,
0)
-
#define alloc_bootmem_low_pages_nopanic(x) \
-
__alloc_bootmem_low_nopanic(x, PAGE_SIZE,
0)
-
#define alloc_bootmem_low_pages(x) \
-
__alloc_bootmem_low(x, PAGE_SIZE,
0)
-
#define alloc_bootmem_low_pages_node(pgdat, x) \
-
__alloc_bootmem_low_node(pgdat, x, PAGE_SIZE,
0)
讲解一个bootmem内存分配方法:
-
/**参数的作用
-
* __alloc_bootmem - allocate boot memory
-
*
@size
: size of the request in bytes
-
*
@align
: alignment of the region
-
*
@goal
: preferred starting address of the region
-
*
-
* The goal is dropped if it can not be satisfied and the allocation will
-
* fall back to memory below
@goal
.
-
*
-
* Allocation may happen on any node in the system.
-
*
-
* The function panics if the request can not be satisfied.
-
*/
-
void *
__init __alloc_bootmem(unsigned long size, unsigned long align,
-
unsigned
long
goal)
-
{
-
unsigned
long limit =
0;
-
-
return ___alloc_bootmem(size, align, goal, limit);
-
}
-
static
void * __init ___alloc_bootmem(
unsigned
long size,
unsigned
long align,
unsigned
long goal,
unsigned
long limit)
-
{
-
void *mem = ___alloc_bootmem_nopanic(size, align, goal, limit);
-
-
if (mem)
-
return mem;
-
printk(KERN_ALERT
"bootmem alloc of %lu bytes failed!\n", size);
-
panic(
"Out of memory");
-
return
NULL;
-
}
-
static
void * __init ___alloc_bootmem_nopanic(
unsigned
long size,
-
unsigned
long align,
-
unsigned
long goal,
-
unsigned
long limit)
-
{
-
void *ptr;
-
-
restart:
-
ptr = alloc_bootmem_core(size, align, goal, limit);
-
if (ptr)
-
return ptr;
-
if (goal) {
-
goal =
0;
-
goto restart;
-
}
-
return
NULL;
-
}
-
static
void * __
init alloc_bootmem_core(unsigned long size,
-
unsigned
long
align,
-
unsigned
long
goal,
-
unsigned
long
limit)
-
{
-
bootmem_data_t *bdata;
-
void *region;
-
//循环查找bdata_list,在bootmem中查找一个合适的内存区域
-
list_for_each_entry(bdata, &bdata_list,
list) {
-
if (goal && bdata->node_low_pfn <= PFN_DOWN(goal))
-
continue;
-
if (limit && bdata->node_min_pfn >= PFN_DOWN(limit))
-
break;
-
//然后调用alloc_bootmem_bdata进行分配
-
region = alloc_bootmem_bdata(bdata, size, align, goal, limit);
-
if (region)
-
return region;
-
}
-
return
NULL;
-
}
-
static
void * __
init alloc_bootmem_bdata(struct bootmem_data *bdata,unsigned long size, unsigned long align,unsigned long goal, unsigned long limit)
-
{
-
unsigned
long fallback =
0;
-
unsigned
long min, max, start, sidx, midx, step;
-
//如果size为0,报错
-
BUG_ON(!size);
-
BUG_ON(align & (align -
1));
-
//limit存在并且goal+size>limit报错
-
BUG_ON(limit && goal + size > limit);
-
//如果bdata的bootmem为空,则报错
-
if (!bdata->node_bootmem_map)
-
return
NULL;
-
//获取最小,最大地址
-
min = bdata->node_min_pfn;
-
max = bdata->node_low_pfn;
-
//得到goal和limit的物理地址
-
goal >>= PAGE_SHIFT;
-
limit >>= PAGE_SHIFT;
-
-
if (limit && max > limit)
-
max = limit;
-
if (max <= min)
-
return
NULL;
-
//设置步长,也就是在位图操作时,每次跳过几位
-
step = max(align >> PAGE_SHIFT,
1UL);
-
//goal为指定的查找区域的开始地址,在这里把goal的对齐格式更新后,赋值给start
-
if (goal && min < goal && goal < max)
-
start = ALIGN(goal, step);
-
else
-
start = ALIGN(min, step);
-
//得到查找区域相对于bdata开始位置的偏移,以及获取最大偏移
-
sidx = start - bdata->node_min_pfn;
-
midx = max - bdata->node_min_pfn;
-
//hint为上次分配的偏移
-
if (bdata->hint_idx > sidx) {
-
/*
-
* Handle the valid case of sidx being zero and still
-
* catch the fallback below.
-
*/
-
fallback = sidx +
1;
-
sidx = align_idx(bdata, bdata->hint_idx, step);
-
}
-
-
while (
1) {
-
int merge;
-
void *region;
-
unsigned
long eidx, i, start_off, end_off;
-
find_block:
-
//在位图中(node_bootmem_map)中查找下一个为零的地方,为零代表可用
-
sidx = find_next_zero_bit(bdata->node_bootmem_map, midx, sidx);
-
sidx = align_idx(bdata, sidx, step);
-
eidx = sidx + PFN_UP(size);
-
-
if (sidx >= midx || eidx > midx)
-
break;
-
-
for (i = sidx; i < eidx; i++)
-
if (test_bit(i, bdata->node_bootmem_map)) {
-
sidx = align_idx(bdata, i, step);
-
if (sidx == i)
-
sidx += step;
-
goto find_block;
-
}
-
//这里的start_off即为分配的物理地址开始
-
if (bdata->last_end_off & (PAGE_SIZE -
1) &&
-
PFN_DOWN(bdata->last_end_off) +
1 == sidx)
-
start_off = align_off(bdata, bdata->last_end_off, align);
-
else
-
start_off = PFN_PHYS(sidx);
-
-
//这里的merge是什么作用????????????????
-
merge = PFN_DOWN(start_off) < sidx;
-
//end_off分配的物理地址的结束地址
-
end_off = start_off + size;
-
//把end_off赋值给last_end_off;last_and_off表示上次分配的页内偏移,分配完,其为0
-
bdata->last_end_off = end_off;
-
//hint表示上次分配的页号
-
bdata->hint_idx = PFN_UP(end_off);
-
-
//把分配的地址设置为已配置
-
if (__reserve(bdata, PFN_DOWN(start_off) + merge,
-
PFN_UP(end_off), BOOTMEM_EXCLUSIVE))
-
BUG();
-
//region表示分配地址的开始,清零后返回
-
region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) +
-
start_off);
-
memset(region,
0, size);
-
kmemleak_alloc(region, size,
0,
0);
-
return region;
-
}
-
-
if (fallback) {
-
sidx = align_idx(bdata, fallback -
1, step);
-
fallback =
0;
-
goto find_block;
-
}
-
return
NULL;
-
}