linux内存模型之buddy(伙伴)系统三从bootmem到buddy的过渡
来源:互联网 发布:如何在淘宝网上开店铺 编辑:程序博客网 时间:2024/05/21 06:30
===================================
本文系本站原创,欢迎转载!
转载请注明出处: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);不明思议,放所有的没有使用的内存到空闲链表,
具体到会检查所有的bank,以MAX_ORDER_NR_PAGES为结尾,
调用free_memmap(prev_bank_end, bank_start);释放每个bank的内存到空闲链表,
重点函数
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_SPARSEMEMif (!IS_ALIGNED(prev_bank_end, PAGES_PER_SECTION))free_memmap(prev_bank_end, ALIGN(prev_bank_end, PAGES_PER_SECTION));#endif}
这个写的真的很详细,不忍心再写了。主要判断bank间的空隙,标记他们,
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;}
看一下他们调用的公共函数:__free_pages_bootmem(pfn_to_page(start), order);
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
- linux内存模型之buddy(伙伴)系统三从bootmem到buddy的过渡
- linux内存模型之buddy(伙伴)系统三从bootmem到buddy的过渡
- Linux内存初始化:bootmem到buddy的过渡
- xv6的buddy(伙伴)系统源代码之buddy.h
- linux内存模型之buddy(伙伴)系统一概览及相关数据结构
- linux内存模型之buddy(伙伴)系统二分配与释放
- Buddy System,伙伴系统
- buddy伙伴系统
- Java实现模拟伙伴系统(Buddy system)内存分配
- Linux内存分配alloc_page和__get_free_page详注(伙伴管理系统Buddy)
- 全面解析Linux 内核 3.10.x - 内存管理 - 伙伴系统算法(Buddy System)
- 内存管理之伙伴系统算法(The Buddy System Algorithm)
- Linux内存管理(2) - buddy系统
- Linux kernel memory management buddy system (linux内核内存管理的伙伴算法)
- bootmem & buddy Allocator
- 内存管理算法--Buddy伙伴算法
- 内存管理算法--Buddy伙伴算法
- 内存管理算法--Buddy伙伴算法
- LINUX softraid 管理
- Google的产品质量之道
- Linux命令详解
- 数据结构排序算法总结
- FlashBuilder 和 PureMVC(一)
- linux内存模型之buddy(伙伴)系统三从bootmem到buddy的过渡
- 我的 android listview 图片 内存溢出 解决方案
- PowerDesigner使用技巧
- dos2unix和unix2dos命令使用
- WPF 常用控件 总结 之一
- 百度map的简单例子
- linux 安装oracle9i
- Windows7远程桌面无法全屏显示解决办法
- 提高网站页面加载速度的黄金守则