===================================
本文系本站原创,欢迎转载!
转载请注明出处:http://blog.csdn.net/gdt_A20
===================================
摘要:
buddy基本模型建立起来后,bootmem也将要退出历史舞台了,boomem管理的内存都要过渡到buddy中,同样高端部分内存也要加入进去,因为boomem
毕竟也只是管理了低端部分。以前面的释放函数为基础,理解起来过渡阶段的代码也就不难了。
一、mem_init 函数
先来看一下mem_init函数,
arch/arm/mm/init.c
mm_init-->mem_init()
本着去其糟粕,取其精华的原则,挑重点的看吧,
max_mapnr = pfn_to_page(max_pfn + PHYS_PFN_OFFSET) - mem_map; //PHYS_PFN_OFFSET被定义成 #define PHYS_PFN_OFFSET (PHYS_OFFSET >> PAGE_SHIFT)PHYS_PFN_OFFSET是在直接映射中的第一个页,max_pfn是在bootmem分配器时候提到的总内存页数,
max_pfn = max_high - PHYS_PFN_OFFSET;
这样算回来,就得到了最大内存在数组中的标号,
减去数组起始地址得到总共有多少页。
下一个函数
/* this will put all unused low memory onto the freelists */ free_unused_memmap(&meminfo);不明思议,放所有的没有使用的内存到空闲链表,
重点函数
totalram_pages += free_all_bootmem();释放bootmem中所有空闲页到buddy分配器,完成过渡
free_highpages();释放所有高端内存页到buddy;
for_each_bank(i, &meminfo) { struct membank *bank = &meminfo.bank[i]; unsigned int pfn1, pfn2; struct page *page, *end; pfn1 = bank_pfn_start(bank); pfn2 = bank_pfn_end(bank); page = pfn_to_page(pfn1); end = pfn_to_page(pfn2 - 1) + 1; do { if (PageReserved(page)) reserved_pages++; else if (!page_count(page)) free_pages++; page++; } while (page < end); }
根据meminfo记录的bank信息,检查页的状态,后面会打印详细的内存状态信息,这里就不罗列出来了!
1、free_unused_memmap 函数
/* * The mem_map array can get very big. Free the unused area of the memory map. */ static void __init free_unused_memmap(struct meminfo *mi) { unsigned long bank_start, prev_bank_end = 0; unsigned int i; /* * This relies on each bank being in address order. * The banks are sorted previously in bootmem_init(). */ for_each_bank(i, mi) { struct membank *bank = &mi->bank[i]; bank_start = bank_pfn_start(bank); #ifdef CONFIG_SPARSEMEM /* * Take care not to free memmap entries that don't exist * due to SPARSEMEM sections which aren't present. */ bank_start = min(bank_start, ALIGN(prev_bank_end, PAGES_PER_SECTION)); #else /* * Align down here since the VM subsystem insists that the * memmap entries are valid from the bank start aligned to * MAX_ORDER_NR_PAGES. */ bank_start = round_down(bank_start, MAX_ORDER_NR_PAGES); #endif /* * If we had a previous bank, and there is a space * between the current bank and the previous, free it. */ if (prev_bank_end && prev_bank_end < bank_start) free_memmap(prev_bank_end, bank_start); /* * Align up here since the VM subsystem insists that the * memmap entries are valid from the bank end aligned to * MAX_ORDER_NR_PAGES. */ prev_bank_end = ALIGN(bank_pfn_end(bank), MAX_ORDER_NR_PAGES); } #ifdef CONFIG_SPARSEMEM if (!IS_ALIGNED(prev_bank_end, PAGES_PER_SECTION)) free_memmap(prev_bank_end, ALIGN(prev_bank_end, PAGES_PER_SECTION)); #endif }
free_memmap调用mark_bootmem标记page为unused,放到后面释放掉.
2.free_all_bootmem()函数
/** * free_all_bootmem - release free pages to the buddy allocator * * Returns the number of pages actually released. */ unsigned long __init free_all_bootmem(void) { unsigned long total_pages = 0; bootmem_data_t *bdata; list_for_each_entry(bdata, &bdata_list, list) total_pages += free_all_bootmem_core(bdata); return total_pages; }
进入所有节点进行释放,返回释放总页数;
mm/bootmem.c中:
static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) { int aligned; struct page *page; unsigned long start, end, pages, count = 0; if (!bdata->node_bootmem_map) //bit位图不存在,退出 return 0; start = bdata->node_min_pfn; end = bdata->node_low_pfn; /* * If the start is aligned to the machines wordsize, we might * be able to free pages in bulks of that order. */ aligned = !(start & (BITS_PER_LONG - 1)); bdebug("nid=%td start=%lx end=%lx aligned=%d\n", bdata - bootmem_node_data, start, end, aligned); while (start < end) { //够32bit,一个long长度的,按32释放,不然后面单独释放 unsigned long *map, idx, vec; map = bdata->node_bootmem_map; //得到bit位图 idx = start - bdata->node_min_pfn; //最低页号 /* * 找出对应位图所在位置(先找出对应的long型数), * 比如如果是33,那么33/32 == 1,那么就在map[1]中,map[0]表示到32,然后将其取反 */ vec = ~map[idx / BITS_PER_LONG]; /* 必须保证对其,结束页大于开始页+32 */ if (aligned && vec == ~0UL && start + BITS_PER_LONG < end) { int order = ilog2(BITS_PER_LONG); //得到order,也就是2的多少次方,此时都是空闲,那么以这个为基数进行释放 /* 根据pfn找到对应的页,进行处理 */ __free_pages_bootmem(pfn_to_page(start), order); count += BITS_PER_LONG; //计数器循环+32 } else { unsigned long off = 0; /* 如果不满足都为空,那么找到空的页,单独释放掉,此时order为0 * 释放到冷热页缓存 */ while (vec && off < BITS_PER_LONG) { if (vec & 1) { page = pfn_to_page(start + off); __free_pages_bootmem(page, 0); count++; } vec >>= 1; off++; } } start += BITS_PER_LONG; //每一次过渡32个页 } /* 将位图部分同样释放到buddy */ page = virt_to_page(bdata->node_bootmem_map); pages = bdata->node_low_pfn - bdata->node_min_pfn; pages = bootmem_bootmap_pages(pages); count += pages; while (pages--) __free_pages_bootmem(page++, 0); bdebug("nid=%td released=%lx\n", bdata - bootmem_node_data, count); return count; }
mm/page_alloc.c
/* * permit the bootmem allocator to evade page validation on high-order frees */ void __meminit __free_pages_bootmem(struct page *page, unsigned int order) { if (order == 0) { __ClearPageReserved(page); //清除该页的保留标志 set_page_count(page, 0); //引用计数清0 set_page_refcounted(page); __free_page(page); //释放该页到冷热页缓存 } else { //order不是0用于释放32的整数倍页 int loop; prefetchw(page); for (loop = 0; loop < BITS_PER_LONG; loop++) { struct page *p = &page[loop]; //以该页为起始循环设置对应page if (loop + 1 < BITS_PER_LONG) prefetchw(p + 1); __ClearPageReserved(p); //清除其预留标志 set_page_count(p, 0); //设置count } set_page_refcounted(page); __free_pages(page, order); //order不是0的释放 } }
这里存在两个函数,一个是__free_page(page),另一个是__free_pages(page, order),
分别看一下,
mm/page_alloc.c
void __free_pages(struct page *page, unsigned int order) { if (put_page_testzero(page)) { if (order == 0) free_hot_cold_page(page, 0); //order为0的时候释放到冷热页 else __free_pages_ok(page, order); //否则释放到伙伴系统 } }而__free_page(page)被定义为
linux/gfp.h
#define __free_page(page) __free_pages((page), 0)同样是调用了__free_pages,
__free_pages前文已经说过,这里不再赘述;
3.free_highpages
下面看一下高端内存的填补;
arch/arm/mm/init.c
static void __init free_highpages(void) { #ifdef CONFIG_HIGHMEM //高端内存部分是可以配置的 unsigned long max_low = max_low_pfn + PHYS_PFN_OFFSET; //得到高端内存地址 struct memblock_region *mem, *res; /* set highmem page free */ for_each_memblock(memory, mem) { //memory,用于描述一些高端内存信息,unsigned long start = memblock_region_memory_base_pfn(mem); //有的时候高端内存只不过是一些寄存器存在那里, unsigned long end = memblock_region_memory_end_pfn(mem); //得到基地址和结束地址 /* Ignore complete lowmem entries */ if (end <= max_low) //忽略不足高端最低配置部分 continue; /* Truncate partial highmem entries */ if (start < max_low) //规范最低地址 start = max_low; /* Find and exclude any reserved regions */ for_each_memblock(reserved, res) { //去除中间保留部分,如果有保留,分成几部分释放 unsigned long res_start, res_end; res_start = memblock_region_reserved_base_pfn(res); res_end = memblock_region_reserved_end_pfn(res); if (res_end < start) continue; if (res_start < start) res_start = start; if (res_start > end) res_start = end; if (res_end > end) res_end = end; if (res_start != start) totalhigh_pages += free_area(start, res_start, NULL); start = res_end; if (start == end) break; } /* And now free anything which remains */ if (start < end) totalhigh_pages += free_area(start, end, NULL); //释放掉 } totalram_pages += totalhigh_pages; #endif }
static inline int free_area(unsigned long pfn, unsigned long end, char *s) { unsigned int pages = 0, size = (end - pfn) << (PAGE_SHIFT - 10); for (; pfn < end; pfn++) { struct page *page = pfn_to_page(pfn); //释放对应的页 ClearPageReserved(page); init_page_count(page); __free_page(page); pages++; } if (size && s) printk(KERN_INFO "Freeing %s memory: %dK\n", s, size); return pages; }
三、总结
结合前面的分配释放,描述了从bootmem到buddy的过度,主要是bootmem时候内存的释放,以及高端内存的填充;
Thanks