原本由bootmem管理的内存在mem_init函数中交由伙伴系统管理。
1.free_unused_memmap_node
相邻的membank间可能存在空洞,但在bootmem阶段这些空洞页也分配了page结构体。该函数的作用是找到这些page结构体所占用的内存并且释放掉。
static void __init free_unused_memmap_node(int node, struct meminfo *mi) -->unsigned long bank_start; //前一个membank的结束地址 -->unsigned long prev_bank_end ;//后一个membank的起始地址 -->free_memmap(node, prev_bank_end, bank_start); -->struct page *start_pg = pfn_to_page(prev_bank_end); -->struct page *end_pg = pfn_to_page(bank_start); -->unsigned long pg = PAGE_ALIGN(__pa(start_pg)); -->unsigned long pgend = __pa(end_pg) & PAGE_MASK; -->free_bootmem_node(NODE_DATA(node), pg, pgend - pg);
2.移交bootmem分配剩余的空闲页到伙伴系统
该函数返回的是返回给伙伴系统的空闲页数。
unsigned long __init free_all_bootmem_node(pg_data_t *pgdat) { register_page_bootmem_info_node(pgdat); return free_all_bootmem_core(pgdat->bdata); }
空闲页保存在全局变量totalram_pages中
/*mm/page_alloc.c*/ unsigned long totalram_pages __read_mostly;
totalram_pages += free_all_bootmem_node(pgdat);
2.1该函数的核心是free_all_bootmem_core(pgdat->bdata)
static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) -->__free_pages_bootmem(pfn_to_page(start), order); -->释放掉bdata->node_bootmem_map所占用的内存,彻底废弃bootmem分配器
释放页到伙伴系统底层调用的两个函数是
void __free_pages(struct page *page, unsigned int order) #define __free_page(page) __free_pages((page), 0)
可以看到,最终调用的还是__free_pages函数
void __free_pages(struct page *page, unsigned int order) { if (put_page_testzero(page)) { if (order == 0) free_hot_page(page); else __free_pages_ok(page, order); } }
free_hot_page流程如下
free_hot_page(page); -->free_hot_cold_page(page, 0); -->struct zone *zone = page_zone(page); -->struct per_cpu_pages *pcp = &zone_pcp(zone, get_cpu())->pcp; -->if (cold) list_add_tail(&page->lru, &pcp->list);/*冷页插入队列末尾*/ else list_add(&page->lru, &pcp->list);/*热页插入队列首*/ -->pcp->count++; -->if (pcp->count >= pcp->high) -->free_pages_bulk(zone, pcp->batch, &pcp->list, 0); -->从队列末尾开始删除pcp->batch个页 -->__free_one_page(page, zone, order);/*把删除的页释放到伙伴系统*/
__free_pages_ok流程如下:
static void __free_pages_ok(struct page *page, unsigned int order) -->free_one_page(page_zone(page), page, order); -->__free_one_page(page, zone, order);
可以看出,最终调用相同的底层函数__free_one_page,这个函数的实现可以说是伙伴系统的精髓。
这里注意空闲页加入伙伴系统后要做如下的设置。
set_page_order(page, order); -->set_page_private(page, order);/*设置page->private*/ -->__SetPageBuddy(page);/*设置page->flags*/ list_add(&page->lru,&zone->free_area[order].free_list[migratetype]); zone->free_area[order].nr_free++;