- 论坛徽章:
- 0
|
linux启动内存分配器
linux启动内存分配器是在伙伴系统、slab机制实现之前,为满足内核中内存的分配而建立的。本身的机制比较简单,使用位图来进行标志分配和释放。
一、数据结构介绍
1,保留区间
因为在建立启动内存分配器的时候,会涉及保留内存。也就是说,之前保留给页表、分配器本身(用于映射的位图)、io等得内存在分配器建立后,当用它来分配内存空间时,保留出来的那些部分就不能再分配了。linux中对保留内存空间的部分用下列数据结构表示
- view plaincopy to clipboardprint?/*
- * Early reserved memory areas.
- */
- #define MAX_EARLY_RES 20/*保留空间最大块数*/
-
- struct early_res {/*保留空间结构*/
- u64 start, end;
- char name[16];
- char overlap_ok;
- };
- /*保留内存空间全局变量*/
- static struct early_res early_res[MAX_EARLY_RES] __initdata = {
- { 0, PAGE_SIZE, "BIOS data page" }, /* BIOS data page */
- {}
- };
- /*
- * Early reserved memory areas.
- */
- #define MAX_EARLY_RES 20/*保留空间最大块数*/
- struct early_res {/*保留空间结构*/
- u64 start, end;
- char name[16];
- char overlap_ok;
- };
- /*保留内存空间全局变量*/
- static struct early_res early_res[MAX_EARLY_RES] __initdata = {
- { 0, PAGE_SIZE, "BIOS data page" }, /* BIOS data page */
- {}
- };2,bootmem分配器
复制代码- view plaincopy to clipboardprint?/*
- * node_bootmem_map is a map pointer - the bits represent all physical
- * memory pages (including holes) on the node.
- */
- /*用于bootmem分配器的节点数据结构*/
- typedef struct bootmem_data {
- unsigned long node_min_pfn;/*存放bootmem位图的第一个页面(即内核映象结束处的第一个页面)。*/
- unsigned long node_low_pfn;/*物理内存的顶点,最高不超过896MB。*/
- void *node_bootmem_map;
- unsigned long last_end_off;/*用来存放在前一次分配中所分配的最后一个字节相对于last_pos的位移量*/
- unsigned long hint_idx;/*存放前一次分配的最后一个页面号*/
- struct list_head list;
- } bootmem_data_t;
- /*
- * node_bootmem_map is a map pointer - the bits represent all physical
- * memory pages (including holes) on the node.
- */
- /*用于bootmem分配器的节点数据结构*/
- typedef struct bootmem_data {
- unsigned long node_min_pfn;/*存放bootmem位图的第一个页面(即内核映象结束处的第一个页面)。*/
- unsigned long node_low_pfn;/*物理内存的顶点,最高不超过896MB。*/
- void *node_bootmem_map;
- unsigned long last_end_off;/*用来存放在前一次分配中所分配的最后一个字节相对于last_pos的位移量*/
- unsigned long hint_idx;/*存放前一次分配的最后一个页面号*/
- struct list_head list;
- } bootmem_data_t;
复制代码 全局链表- view plaincopy to clipboardprint?static struct list_head bdata_list __initdata = LIST_HEAD_INIT(bdata_list);
- static struct list_head bdata_list __initdata = LIST_HEAD_INIT(bdata_list);
复制代码 二、启动分配器的建立
启动分配器的建立主要的流程为初始化映射位图、活动内存区的映射位置0(表示可用)、保留内存区域处理,其中保留区存放在上面介绍的全局数组中,这里只是将分配器中对应映射位图值1,表示已经分配。
下面我们看内核中具体的初始化流程。
[code]start_kernel()->setup_arch()->initmem_init()
view plaincopy to clipboardprint?void __init setup_arch(char **cmdline_p)
{
.......
<span style="white-space: pre; "> </span>/*此函数在开始对bootmem分配制度建立做些准备工作
然后调用相关函数建立bootmem分配制度*/
initmem_init(0, max_pfn);
.......
}
void __init setup_arch(char **cmdline_p)
{
.......
<span style="white-space: pre; "> </span>/*此函数在开始对bootmem分配制度建立做些准备工作
然后调用相关函数建立bootmem分配制度*/
initmem_init(0, max_pfn);
.......
}
view plaincopy to clipboardprint?<span style="font-family: Arial, Verdana, sans-serif; "><span style="white-space: normal; "></span></span>
<span style="font-family: Arial, Verdana, sans-serif; "><span style="white-space: normal; "></span></span>view plaincopy to clipboardprint?<span style="font-family: Arial, Verdana, sans-serif; "><span style="white-space: normal; "></span></span><pre name="code" class="cpp">void __init initmem_init(unsigned long start_pfn,
unsigned long end_pfn)
{
#ifdef CONFIG_HIGHMEM
highstart_pfn = highend_pfn = max_pfn;
if (max_pfn > max_low_pfn)
highstart_pfn = max_low_pfn;
/*将活动内存放到early_node_map中,前面已经分析过了*/
e820_register_active_regions(0, 0, highend_pfn);
/*设置上面变量中的内存为当前,在这里没有
设置相关的宏*/
sparse_memory_present_with_active_regions(0);
printk(KERN_NOTICE "%ldMB HIGHMEM available.\n",
pages_to_mb(highend_pfn - highstart_pfn));
num_physpages = highend_pfn;
/*高端内存开始地址物理*/
high_memory = (void *) __va(highstart_pfn * PAGE_SIZE - 1) + 1;
#else
e820_register_active_regions(0, 0, max_low_pfn);
sparse_memory_present_with_active_regions(0);
num_physpages = max_low_pfn;
high_memory = (void *) __va(max_low_pfn * PAGE_SIZE - 1) + 1;
#endif
#ifdef CONFIG_FLATMEM
max_mapnr = num_physpages;
#endif
__vmalloc_start_set = true;
printk(KERN_NOTICE "%ldMB LOWMEM available.\n",
pages_to_mb(max_low_pfn));
/*安装bootmem分配器,此分配器在伙伴系统起来之前
用来进行承担内存的分配等管理*/
setup_bootmem_allocator();
}
<span style="font-family: Arial, Verdana, sans-serif; "><span style="white-space: normal; "></span></span><pre name="code" class="cpp">void __init initmem_init(unsigned long start_pfn,
unsigned long end_pfn)
{
#ifdef CONFIG_HIGHMEM
highstart_pfn = highend_pfn = max_pfn;
if (max_pfn > max_low_pfn)
highstart_pfn = max_low_pfn;
/*将活动内存放到early_node_map中,前面已经分析过了*/
e820_register_active_regions(0, 0, highend_pfn);
/*设置上面变量中的内存为当前,在这里没有
设置相关的宏*/
sparse_memory_present_with_active_regions(0);
printk(KERN_NOTICE "%ldMB HIGHMEM available.\n",
pages_to_mb(highend_pfn - highstart_pfn));
num_physpages = highend_pfn;
/*高端内存开始地址物理*/
high_memory = (void *) __va(highstart_pfn * PAGE_SIZE - 1) + 1;
#else
e820_register_active_regions(0, 0, max_low_pfn);
sparse_memory_present_with_active_regions(0);
num_physpages = max_low_pfn;
high_memory = (void *) __va(max_low_pfn * PAGE_SIZE - 1) + 1;
#endif
#ifdef CONFIG_FLATMEM
max_mapnr = num_physpages;
#endif
__vmalloc_start_set = true;
printk(KERN_NOTICE "%ldMB LOWMEM available.\n",
pages_to_mb(max_low_pfn));
/*安装bootmem分配器,此分配器在伙伴系统起来之前
用来进行承担内存的分配等管理*/
setup_bootmem_allocator();
}
view plaincopy to clipboardprint?void __init setup_bootmem_allocator(void)
{
int nodeid;
unsigned long bootmap_size, bootmap;
/*
* Initialize the boot-time allocator (with low memory only):
*/
/*计算所需要的映射页面大小一个字节一位,
所以需要对总的页面大小除以8*/
bootmap_size = bootmem_bootmap_pages(max_low_pfn)<<PAGE_SHIFT;
/*直接中e820中找到一个大小合适的内存块,返回基址*/
bootmap = find_e820_area(0, max_pfn_mapped<<PAGE_SHIFT, bootmap_size,
PAGE_SIZE);
if (bootmap == -1L)
panic("Cannot find bootmem map of size %ld\n", bootmap_size);
/*将用于位图映射的页面保留*/
reserve_early(bootmap, bootmap + bootmap_size, "BOOTMAP");
printk(KERN_INFO " mapped low ram: 0 - %08lx\n",
max_pfn_mapped<<PAGE_SHIFT);
printk(KERN_INFO " low ram: 0 - %08lx\n", max_low_pfn<<PAGE_SHIFT);
/*对每一个在线的node*/
for_each_online_node(nodeid) {
unsigned long start_pfn, end_pfn;
#ifdef CONFIG_NEED_MULTIPLE_NODES/*not set*/
start_pfn = node_start_pfn[nodeid];
end_pfn = node_end_pfn[nodeid];
if (start_pfn > max_low_pfn)
continue;
if (end_pfn > max_low_pfn)
end_pfn = max_low_pfn;
#else
start_pfn = 0;
end_pfn = max_low_pfn;
#endif
/*对指定节点安装启动分配器*/
bootmap = setup_node_bootmem(nodeid, start_pfn, end_pfn,
bootmap);
}
/*bootmem的分配制度到这里就已经建立完成,把after_bootmem
变量置成1,标识*/
after_bootmem = 1;
}
void __init setup_bootmem_allocator(void)
{
int nodeid;
unsigned long bootmap_size, bootmap;
/*
* Initialize the boot-time allocator (with low memory only):
*/
/*计算所需要的映射页面大小一个字节一位,
所以需要对总的页面大小除以8*/
bootmap_size = bootmem_bootmap_pages(max_low_pfn)<<PAGE_SHIFT;
/*直接中e820中找到一个大小合适的内存块,返回基址*/
bootmap = find_e820_area(0, max_pfn_mapped<<PAGE_SHIFT, bootmap_size,
PAGE_SIZE);
if (bootmap == -1L)
panic("Cannot find bootmem map of size %ld\n", bootmap_size);
/*将用于位图映射的页面保留*/
reserve_early(bootmap, bootmap + bootmap_size, "BOOTMAP");
printk(KERN_INFO " mapped low ram: 0 - %08lx\n",
max_pfn_mapped<<PAGE_SHIFT);
printk(KERN_INFO " low ram: 0 - %08lx\n", max_low_pfn<<PAGE_SHIFT);
/*对每一个在线的node*/
for_each_online_node(nodeid) {
unsigned long start_pfn, end_pfn;
#ifdef CONFIG_NEED_MULTIPLE_NODES/*not set*/
start_pfn = node_start_pfn[nodeid];
end_pfn = node_end_pfn[nodeid];
if (start_pfn > max_low_pfn)
continue;
if (end_pfn > max_low_pfn)
end_pfn = max_low_pfn;
#else
start_pfn = 0;
end_pfn = max_low_pfn;
#endif
/*对指定节点安装启动分配器*/
bootmap = setup_node_bootmem(nodeid, start_pfn, end_pfn,
bootmap);
}
/*bootmem的分配制度到这里就已经建立完成,把after_bootmem
变量置成1,标识*/
after_bootmem = 1;
}view plaincopy to clipboardprint?static unsigned long __init setup_node_bootmem(int nodeid,
unsigned long start_pfn,
unsigned long end_pfn,
unsigned long bootmap)
{
unsigned long bootmap_size;
/* don't touch min_low_pfn */
/*初始化映射位图,将位图中的所有位置1*/
bootmap_size = init_bootmem_node(NODE_DATA(nodeid),
bootmap >> PAGE_SHIFT,
start_pfn, end_pfn);
printk(KERN_INFO " node %d low ram: %08lx - %08lx\n",
nodeid, start_pfn<<PAGE_SHIFT, end_pfn<<PAGE_SHIFT);
printk(KERN_INFO " node %d bootmap %08lx - %08lx\n",
nodeid, bootmap, bootmap + bootmap_size);
/*将活动内存区对应位图相关位置0,表示可被分配的*/
free_bootmem_with_active_regions(nodeid, end_pfn);
/*对置保留位的相关页面对应的位图设置为1,表示已经分配
或者不可用(不能被分配)*/
early_res_to_bootmem(start_pfn<<PAGE_SHIFT, end_pfn<<PAGE_SHIFT);
/*返回映射页面的最后地址,下次映射即可以从这里开始*/
return bootmap + bootmap_size;
}
static unsigned long __init setup_node_bootmem(int nodeid,
unsigned long start_pfn,
unsigned long end_pfn,
unsigned long bootmap)
{
unsigned long bootmap_size;
/* don't touch min_low_pfn */
/*初始化映射位图,将位图中的所有位置1*/
bootmap_size = init_bootmem_node(NODE_DATA(nodeid),
bootmap >> PAGE_SHIFT,
start_pfn, end_pfn);
printk(KERN_INFO " node %d low ram: %08lx - %08lx\n",
nodeid, start_pfn<<PAGE_SHIFT, end_pfn<<PAGE_SHIFT);
printk(KERN_INFO " node %d bootmap %08lx - %08lx\n",
nodeid, bootmap, bootmap + bootmap_size);
/*将活动内存区对应位图相关位置0,表示可被分配的*/
free_bootmem_with_active_regions(nodeid, end_pfn);
/*对置保留位的相关页面对应的位图设置为1,表示已经分配
或者不可用(不能被分配)*/
early_res_to_bootmem(start_pfn<<PAGE_SHIFT, end_pfn<<PAGE_SHIFT);
/*返回映射页面的最后地址,下次映射即可以从这里开始*/
return bootmap + bootmap_size;
}
对于初始化映射位图,最终调用init_bootmem_core()
view plaincopy to clipboardprint?/*
* Called once to set up the allocator itself.
*/
static unsigned long __init init_bootmem_core(bootmem_data_t *bdata,
unsigned long mapstart, unsigned long start, unsigned long end)
{
unsigned long mapsize;
mminit_validate_memmodel_limits(&start, &end);
bdata->node_bootmem_map = phys_to_virt(PFN_PHYS(mapstart));
bdata->node_min_pfn = start;
bdata->node_low_pfn = end;
/*添加bdata变量到链表中*/
link_bootmem(bdata);
/*
* Initially all pages are reserved - setup_arch() has to
* register free RAM areas explicitly.
*/
/*计算本bdata的mapsize,也就是内存页面大小的1/8*/
mapsize = bootmap_bytes(end - start);
/*将所有map置1*/
memset(bdata->node_bootmem_map, 0xff, mapsize);
bdebug("nid=%td start=%lx map=%lx end=%lx mapsize=%lx\n",
bdata - bootmem_node_data, start, mapstart, end, mapsize);
return mapsize;
}
/*
* Called once to set up the allocator itself.
*/
static unsigned long __init init_bootmem_core(bootmem_data_t *bdata,
unsigned long mapstart, unsigned long start, unsigned long end)
{
unsigned long mapsize;
mminit_validate_memmodel_limits(&start, &end);
bdata->node_bootmem_map = phys_to_virt(PFN_PHYS(mapstart));
bdata->node_min_pfn = start;
bdata->node_low_pfn = end;
/*添加bdata变量到链表中*/
link_bootmem(bdata);
/*
* Initially all pages are reserved - setup_arch() has to
* register free RAM areas explicitly.
*/
/*计算本bdata的mapsize,也就是内存页面大小的1/8*/
mapsize = bootmap_bytes(end - start);
/*将所有map置1*/
memset(bdata->node_bootmem_map, 0xff, mapsize);
bdebug("nid=%td start=%lx map=%lx end=%lx mapsize=%lx\n",
bdata - bootmem_node_data, start, mapstart, end, mapsize);
return mapsize;
}
view plaincopy to clipboardprint?/*
* link bdata in order
*/
/*添加到链表,由添加的代码可知
链表中的数据开始位置为递增的*/
|
|