linux内存模型之buddy(伙伴)系统三从bootmem到buddy的过渡
来源:互联网 发布:罗马里奥实况数据 编辑:程序博客网 时间:2024/06/06 08:32
===================================
本文系本站原创,欢迎转载!
转载请注明出处: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)
这里再给出一下:
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();
- free_highpages();
- 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
- }
这个写的真的很详细,不忍心再写了。主要判断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); //否则释放到伙伴系统
- }
- }
linux/gfp.h
- #define __free_page(page) __free_pages((page), 0)
- __free_pages前文已经说过,这里不再赘述;
- <span style="color:#ff0000;">3.free_highpages</span>
- 下面看一下高端内存的填补;
- arch/arm/mm/init.c
- <pre name="code" class="cpp">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,用于描述一些高端内存信息,</pre><pre name="code" class="cpp"> 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
- }</pre><br>
- <br>
- <pre></pre>
- <p></p>
- <pre></pre>
- <pre name="code" class="cpp"><pre name="code" class="cpp">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;
- }</pre><br>
- <br>
- <pre></pre>
- <p></p>
- <p>三、总结</p>
- <p> 结合前面的分配释放,描述了从bootmem到buddy的过度,主要是bootmem时候内存的释放,以及高端内存的填充;</p>
- <p>Thanks</p>
- <p></p>
- <p></p>
- <p></p>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- </pre>
- 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伙伴算法
- PS制作透明球体
- 互联网产品定位不要盲从领导,预设立场是大忌
- 25 条 SSH 命令和技巧
- 673 - Parentheses Balance
- 条款25:考虑写一个不抛出异常的swap函数
- linux内存模型之buddy(伙伴)系统三从bootmem到buddy的过渡
- WiFi Chat:通过网页与局域网用户聊天
- 魔兽争霸游戏开始前数据包分析
- 9.6
- hdu4279数论
- 442 - Matrix Chain Multiplication
- 9月6号的课
- 11111 - Generalized Matrioshkas
- memcached windows下安装