Bootmem

来源:互联网 发布:ipad同步软件 编辑:程序博客网 时间:2024/05/17 22:29

     Bootmem是系统启动时的内存管理器,当伙伴系统初始化后,bootmem就会被free,存在时间较短,但是在伙伴系统初始化之前的内核其他初始化, 均需要依赖于bootmem,且了解bootmem能够帮助理解伙伴系统。

 

Bootmem使用位图来表示哪些页框被使用吗,例如系统是100兆内存,bootmem需要管理这100兆内存,一个页大小为1k,,总共102400个页(100m/1k),于是就需要102400个比特表示这些页,即需要25600个字节来存储这个位图。低n个比特为1表示该页被使用,为0表示改页未被使用。通过这种方式,来进行对物理内存进行管理,简单有效。

 

 

Bootmem初始化函数:bootmem_init

1:计算可用页的范围(最小min_low_pfn-最大max_low_pfn)。

2:计算可用页的空洞(最小-最大之间哪些区域不可用)

3:设置位图,将可用页对应的bit置为1,不可用页对应的bit置为0

 

/*

 * Init any data related to initrd. It's a nop if INITRD is

 * not selected. Once that done we can determine the low bound

 * of usable memory.

 */

reserved_end = max(init_initrd(),

   (unsigned long) PFN_UP(__pa_symbol(&_end)));

 

计算系统需要预留的页数,这是min_low_pfn取值的判断之一。

 

 

 

 

 


 

 

boot_mem_map是系统启动传进来的关于内存的描述,它定义了内存哪些地址到哪些地址是可以用的,函数print_memory_map在启动时将其打印,dmesg查看,我们根据boot_mem_map来决定min_low_pfnmax_low_pfn的值。

/*

 * Find the highest page frame number we have available.

 */

for (i = 0; i < boot_mem_map.nr_map; i++) {

unsigned long start, end;

 

if (boot_mem_map.map[i].type != BOOT_MEM_RAM)

continue;

 

start = PFN_UP(boot_mem_map.map[i].addr);/*往大的取,即使浪费*/

end = PFN_DOWN(boot_mem_map.map[i].addr/*往小的取,即使浪费*/

+ boot_mem_map.map[i].size);

 

/*记录boot_mem_map中最小的和最大的置*/

if (end > max_low_pfn)

max_low_pfn = end;

if (start < min_low_pfn)

min_low_pfn = start;

if (end <= reserved_end)

continue;

if (start >= mapstart)

continue;

/*mapstart 用于计算位图信息存储的页的地址,当然不能和reserved_end有重叠*/

mapstart = max(reserved_end, start);

}

 

 

/*初始化bootmem*/

bootmap_size = init_bootmem_node(NODE_DATA(0), mapstart,

 min_low_pfn, max_low_pfn);

 

init_bootmem_node:初始化bootmem_data_t

 

 

 

 

 

 

 

 

 

/*

 * 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));/*bootmem位图的地址*/

bdata->node_min_pfn = start;/*记录min*/

bdata->node_low_pfn = end;/*记录max*/

 

link_bootmem(bdata);

 

/*

 * Initially all pages are reserved - setup_arch() has to

 * register free RAM areas explicitly.

 */

/*计算需要多少个字节描述start-end个页 */

mapsize = bootmap_bytes(end - start);

/*默认全部内存不可用*/

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;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

/*计算startend之间的空洞,核心函数是add_active_range*/

for (i = 0; i < boot_mem_map.nr_map; i++) {

unsigned long start, end;

 

start = PFN_UP(boot_mem_map.map[i].addr);

end = PFN_DOWN(boot_mem_map.map[i].addr

+ boot_mem_map.map[i].size);

 

if (start <= min_low_pfn)

start = min_low_pfn;

if (start >= end)

continue;

 

#ifndef CONFIG_HIGHMEM

if (end > max_low_pfn)

end = max_low_pfn;

 

/*

 * ... finally, is the area going away?

 */

if (end <= start)

continue;

#endif

 

add_active_range(0, start, end);

}

 

 

/*

 * Register fully available low RAM pages with the bootmem allocator.

 */

/*根据boot_mem_map,将其描述的可用内存对应的bit0,核心函数是free_bootmem*/

for (i = 0; i < boot_mem_map.nr_map; i++) {

unsigned long start, end, size;

 

/*

 * Reserve usable memory.

 */

if (boot_mem_map.map[i].type != BOOT_MEM_RAM)

continue;

 

start = PFN_UP(boot_mem_map.map[i].addr);

end   = PFN_DOWN(boot_mem_map.map[i].addr

    + boot_mem_map.map[i].size);

/*

 * We are rounding up the start address of usable memory

 * and at the end of the usable range downwards.

 */

if (start >= max_low_pfn)

continue;

if (start < reserved_end)

start = reserved_end;

if (end > max_low_pfn)

end = max_low_pfn;

 

/*

 * ... finally, is the area going away?

 */

if (end <= start)

continue;

size = end - start;

 

/* Register lowmem ranges */

free_bootmem(PFN_PHYS(start), size << PAGE_SHIFT);

memory_present(0, start, end);

}

/*PFN_PHYS(mapstart)起的地址存在着bootmap_size个字节的位图信息,也需要保留*/

reserve_bootmem(PFN_PHYS(mapstart), bootmap_size, BOOTMEM_DEFAULT);

 

reserve_bootmem free_bootmem都是对mark_bootmem 的封装,最后都会调用mark_bootmem_nodemark_bootmem_node会调用__reserve或者__free,里面是对bdata->node_bootmem_mapmapsize字节的地方置0或者置1

 

 

 

static inline void __free_one_page(struct page *page,

struct zone *zone, unsigned int order,

int migratetype)

{

unsigned long page_idx;

unsigned long combined_idx;

unsigned long uninitialized_var(buddy_idx);

struct page *buddy;

 

VM_BUG_ON(!zone_is_initialized(zone));

 

if (unlikely(PageCompound(page)))

if (unlikely(destroy_compound_page(page, order)))

return;

 

VM_BUG_ON(migratetype == -1);

 

/*取余算法,2048(如果MAX_ORDER11

*由伙伴算法知,page是从上一个order中一分为二的,故page_to_pfn(page)必是2 *x次幂,page_idx也是2y次幂。

*/

page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1);

 

/*暂不考虑page_index0的情况(page_index0,不会触发bug_on

*如果pageorder2page_index最小为4,因为order2page必然是order *3page分出来的,即0-8号的page被一分为2,page_index4

*同理如果pageorder3page_index最小为8,即page_index2^xx>=order

*自然 2^x mod 2^order = 0

*/

VM_BUG_ON_PAGE(page_idx & ((1 << order) - 1), page);

VM_BUG_ON_PAGE(bad_range(zone, page), page);

 

while (order < MAX_ORDER-1) {

buddy_idx = __find_buddy_index(page_idx, order);

buddy = page + (buddy_idx - page_idx);

if (!page_is_buddy(page, buddy, order))

break;

/*

 * Our buddy is free or it is CONFIG_DEBUG_PAGEALLOC guard page,

 * merge with it and move up one order.

 */

if (page_is_guard(buddy)) {

clear_page_guard_flag(buddy);

set_page_private(page, 0);

__mod_zone_freepage_state(zone, 1 << order,

  migratetype);

} else {

list_del(&buddy->lru);

zone->free_area[order].nr_free--;

rmv_page_order(buddy);

}

combined_idx = buddy_idx & page_idx;

page = page + (combined_idx - page_idx);

page_idx = combined_idx;

order++;

}

set_page_order(page, order);

 

/*

 * If this is not the largest possible page, check if the buddy

 * of the next-highest order is free. If it is, it's possible

 * that pages are being freed that will coalesce soon. In case,

 * that is happening, add the free page to the tail of the list

 * so it's less likely to be used soon and more likely to be merged

 * as a higher order page

 */

if ((order < MAX_ORDER-2) && pfn_valid_within(page_to_pfn(buddy))) {

struct page *higher_page, *higher_buddy;

combined_idx = buddy_idx & page_idx;

higher_page = page + (combined_idx - page_idx);

buddy_idx = __find_buddy_index(combined_idx, order + 1);

higher_buddy = higher_page + (buddy_idx - combined_idx);

if (page_is_buddy(higher_page, higher_buddy, order + 1)) {

list_add_tail(&page->lru,

&zone->free_area[order].free_list[migratetype]);

goto out;

}

}

 

list_add(&page->lru, &zone->free_area[order].free_list[migratetype]);

out:

zone->free_area[order].nr_free++;

}