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()

本着去其糟粕,取其精华的原则,挑重点的看吧,

[cpp] view plaincopy
  1. max_mapnr   = pfn_to_page(max_pfn + PHYS_PFN_OFFSET) - mem_map;  
  2. //PHYS_PFN_OFFSET被定义成  
  3. #define PHYS_PFN_OFFSET (PHYS_OFFSET >> PAGE_SHIFT)  
PHYS_PFN_OFFSET是在直接映射中的第一个页,max_pfn是在bootmem分配器时候提到的总内存页数,
这里再给出一下:

max_pfn = max_high - PHYS_PFN_OFFSET;

这样算回来,就得到了最大内存在数组中的标号,
减去数组起始地址得到总共有多少页。
下一个函数

[cpp] view plaincopy
  1. /* this will put all unused low memory onto the freelists */  
  2. free_unused_memmap(&meminfo);    
不明思议,放所有的没有使用的内存到空闲链表,
具体到会检查所有的bank,以MAX_ORDER_NR_PAGES为结尾,
调用free_memmap(prev_bank_end, bank_start);释放每个bank的内存到空闲链表,

重点函数

[cpp] view plaincopy
  1. totalram_pages += free_all_bootmem();  
释放bootmem中所有空闲页到buddy分配器,完成过渡
[cpp] view plaincopy
  1. free_highpages();  
释放所有高端内存页到buddy;
[cpp] view plaincopy
  1. for_each_bank(i, &meminfo) {  
  2.         struct membank *bank = &meminfo.bank[i];  
  3.         unsigned int pfn1, pfn2;  
  4.         struct page *page, *end;  
  5.   
  6.         pfn1 = bank_pfn_start(bank);  
  7.         pfn2 = bank_pfn_end(bank);  
  8.   
  9.         page = pfn_to_page(pfn1);  
  10.         end  = pfn_to_page(pfn2 - 1) + 1;  
  11.   
  12.         do {  
  13.             if (PageReserved(page))  
  14.                 reserved_pages++;  
  15.             else if (!page_count(page))  
  16.                 free_pages++;  
  17.             page++;  
  18.         } while (page < end);  
  19.     }  

根据meminfo记录的bank信息,检查页的状态,后面会打印详细的内存状态信息,这里就不罗列出来了!


二、分别详细的看一下上面提到的几个函数。

1、free_unused_memmap 函数

[cpp] view plaincopy
  1. /* 
  2.  * The mem_map array can get very big.  Free the unused area of the memory map. 
  3.  */  
  4. static void __init free_unused_memmap(struct meminfo *mi)  
  5. {  
  6.     unsigned long bank_start, prev_bank_end = 0;  
  7.     unsigned int i;  
  8.   
  9.     /* 
  10.      * This relies on each bank being in address order. 
  11.      * The banks are sorted previously in bootmem_init(). 
  12.      */  
  13.     for_each_bank(i, mi) {  
  14.         struct membank *bank = &mi->bank[i];  
  15.   
  16.         bank_start = bank_pfn_start(bank);  
  17.   
  18. #ifdef CONFIG_SPARSEMEM  
  19.         /* 
  20.          * Take care not to free memmap entries that don't exist 
  21.          * due to SPARSEMEM sections which aren't present. 
  22.          */  
  23.         bank_start = min(bank_start,  
  24.                  ALIGN(prev_bank_end, PAGES_PER_SECTION));  
  25. #else  
  26.         /* 
  27.          * Align down here since the VM subsystem insists that the 
  28.          * memmap entries are valid from the bank start aligned to 
  29.          * MAX_ORDER_NR_PAGES. 
  30.          */  
  31.         bank_start = round_down(bank_start, MAX_ORDER_NR_PAGES);  
  32. #endif  
  33.         /* 
  34.          * If we had a previous bank, and there is a space 
  35.          * between the current bank and the previous, free it. 
  36.          */  
  37.         if (prev_bank_end && prev_bank_end < bank_start)  
  38.             free_memmap(prev_bank_end, bank_start);  
  39.   
  40.         /* 
  41.          * Align up here since the VM subsystem insists that the 
  42.          * memmap entries are valid from the bank end aligned to 
  43.          * MAX_ORDER_NR_PAGES. 
  44.          */  
  45.         prev_bank_end = ALIGN(bank_pfn_end(bank), MAX_ORDER_NR_PAGES);  
  46.     }  
  47.   
  48. #ifdef CONFIG_SPARSEMEM  
  49.     if (!IS_ALIGNED(prev_bank_end, PAGES_PER_SECTION))  
  50.         free_memmap(prev_bank_end,  
  51.                 ALIGN(prev_bank_end, PAGES_PER_SECTION));  
  52. #endif  
  53. }  

这个写的真的很详细,不忍心再写了。主要判断bank间的空隙,标记他们,

free_memmap调用mark_bootmem标记page为unused,放到后面释放掉.

2.free_all_bootmem()函数

[cpp] view plaincopy
  1. /** 
  2.  * free_all_bootmem - release free pages to the buddy allocator 
  3.  * 
  4.  * Returns the number of pages actually released. 
  5.  */  
  6. unsigned long __init free_all_bootmem(void)  
  7. {  
  8. unsigned long total_pages = 0;  
  9. bootmem_data_t *bdata;  
  10.   
  11.   
  12. list_for_each_entry(bdata, &bdata_list, list)     
  13. total_pages += free_all_bootmem_core(bdata);  
  14. return total_pages;  
  15. }  

进入所有节点进行释放,返回释放总页数;

mm/bootmem.c中:

[cpp] view plaincopy
  1. static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata)  
  2. {  
  3.     int aligned;  
  4.     struct page *page;  
  5.     unsigned long start, end, pages, count = 0;  
  6.   
  7.     if (!bdata->node_bootmem_map)                        //bit位图不存在,退出  
  8.         return 0;  
  9.   
  10.     start = bdata->node_min_pfn;  
  11.     end = bdata->node_low_pfn;  
  12.   
  13.     /* 
  14.      * If the start is aligned to the machines wordsize, we might 
  15.      * be able to free pages in bulks of that order. 
  16.      */  
  17.     aligned = !(start & (BITS_PER_LONG - 1));  
  18.   
  19.     bdebug("nid=%td start=%lx end=%lx aligned=%d\n",  
  20.         bdata - bootmem_node_data, start, end, aligned);  
  21.   
  22.     while (start < end) {                                    //够32bit,一个long长度的,按32释放,不然后面单独释放  
  23.         unsigned long *map, idx, vec;  
  24.   
  25.         map = bdata->node_bootmem_map;                   //得到bit位图  
  26.         idx = start - bdata->node_min_pfn;               //最低页号  
  27.   
  28.         /* 
  29.          *  找出对应位图所在位置(先找出对应的long型数), 
  30.          *  比如如果是33,那么33/32 == 1,那么就在map[1]中,map[0]表示到32,然后将其取反  
  31.          */  
  32.                 vec = ~map[idx / BITS_PER_LONG];                   
  33.                                                                    
  34.                 /* 必须保证对其,结束页大于开始页+32 */  
  35.         if (aligned && vec == ~0UL && start + BITS_PER_LONG < end) {  
  36.             int order = ilog2(BITS_PER_LONG);        //得到order,也就是2的多少次方,此时都是空闲,那么以这个为基数进行释放  
  37.   
  38.                         /* 根据pfn找到对应的页,进行处理 */  
  39.             __free_pages_bootmem(pfn_to_page(start), order);    
  40.             count += BITS_PER_LONG;                 //计数器循环+32  
  41.         } else {  
  42.             unsigned long off = 0;  
  43.   
  44.                        /* 如果不满足都为空,那么找到空的页,单独释放掉,此时order为0 
  45.                         * 释放到冷热页缓存 
  46.                         */  
  47.             while (vec && off < BITS_PER_LONG) {     
  48.                 if (vec & 1) {  
  49.                     page = pfn_to_page(start + off);  
  50.                     __free_pages_bootmem(page, 0);  
  51.                     count++;  
  52.                 }  
  53.                 vec >>= 1;  
  54.                 off++;  
  55.             }  
  56.         }  
  57.         start += BITS_PER_LONG;                        //每一次过渡32个页  
  58.     }  
  59.   
  60.         /* 将位图部分同样释放到buddy */  
  61.     page = virt_to_page(bdata->node_bootmem_map);  
  62.     pages = bdata->node_low_pfn - bdata->node_min_pfn;  
  63.     pages = bootmem_bootmap_pages(pages);  
  64.     count += pages;  
  65.     while (pages--)  
  66.         __free_pages_bootmem(page++, 0);  
  67.   
  68.     bdebug("nid=%td released=%lx\n", bdata - bootmem_node_data, count);  
  69.   
  70.     return count;  
  71. }  

看一下他们调用的公共函数:__free_pages_bootmem(pfn_to_page(start), order);

mm/page_alloc.c

[cpp] view plaincopy
  1. /* 
  2.  * permit the bootmem allocator to evade page validation on high-order frees 
  3.  */  
  4. void __meminit __free_pages_bootmem(struct page *page, unsigned int order)  
  5. {  
  6.     if (order == 0) {  
  7.         __ClearPageReserved(page);                //清除该页的保留标志  
  8.         set_page_count(page, 0);                  //引用计数清0  
  9.         set_page_refcounted(page);                   
  10.         __free_page(page);                        //释放该页到冷热页缓存  
  11.     } else {                                      //order不是0用于释放32的整数倍页  
  12.         int loop;  
  13.   
  14.         prefetchw(page);  
  15.         for (loop = 0; loop < BITS_PER_LONG; loop++) {  
  16.             struct page *p = &page[loop];         //以该页为起始循环设置对应page  
  17.   
  18.             if (loop + 1 < BITS_PER_LONG)  
  19.                 prefetchw(p + 1);  
  20.             __ClearPageReserved(p);               //清除其预留标志  
  21.             set_page_count(p, 0);                 //设置count  
  22.         }  
  23.   
  24.         set_page_refcounted(page);  
  25.         __free_pages(page, order);                //order不是0的释放  
  26.     }  
  27. }  

这里存在两个函数,一个是__free_page(page),另一个是__free_pages(page, order),

分别看一下,

mm/page_alloc.c

[cpp] view plaincopy
  1. void __free_pages(struct page *page, unsigned int order)  
  2. {  
  3.     if (put_page_testzero(page)) {  
  4.         if (order == 0)  
  5.             free_hot_cold_page(page, 0);                      //order为0的时候释放到冷热页  
  6.         else  
  7.             __free_pages_ok(page, order);                     //否则释放到伙伴系统  
  8.     }  
  9. }  
而__free_page(page)被定义为

linux/gfp.h

[cpp] view plaincopy
  1. #define  __free_page(page) __free_pages((page), 0)  
同样是调用了__free_pages,
[cpp] view plaincopy
  1. __free_pages前文已经说过,这里不再赘述;  
[cpp] view plaincopy
  1.   
[cpp] view plaincopy
  1. <span style="color:#ff0000;">3.free_highpages</span>  
[cpp] view plaincopy
  1. 下面看一下高端内存的填补;  
[cpp] view plaincopy
  1. arch/arm/mm/init.c  
[cpp] view plaincopy
  1. <pre name="code" class="cpp">static void __init free_highpages(void)  
  2. {  
  3. #ifdef CONFIG_HIGHMEM                                                       //高端内存部分是可以配置的  
  4.     unsigned long max_low = max_low_pfn + PHYS_PFN_OFFSET;              //得到高端内存地址  
  5.     struct memblock_region *mem, *res;  
  6.   
  7.     /* set highmem page free */  
  8.     for_each_memblock(memory, mem) {                                     //memory,用于描述一些高端内存信息,</pre><pre name="code" class="cpp"&gt;      unsigned long start = memblock_region_memory_base_pfn(mem);  //有的时候高端内存只不过是一些寄存器存在那里,  
  9.         unsigned long end = memblock_region_memory_end_pfn(mem);     //得到基地址和结束地址   
  10.   
  11.         /* Ignore complete lowmem entries */  
  12.         if (end <= max_low)                                          //忽略不足高端最低配置部分  
  13.             continue;  
  14.   
  15.         /* Truncate partial highmem entries */  
  16.         if (start < max_low)                                        //规范最低地址  
  17.             start = max_low;  
  18.   
  19.         /* Find and exclude any reserved regions */  
  20.         for_each_memblock(reserved, res) {                         //去除中间保留部分,如果有保留,分成几部分释放  
  21.             unsigned long res_start, res_end;  
  22.   
  23.             res_start = memblock_region_reserved_base_pfn(res);  
  24.             res_end = memblock_region_reserved_end_pfn(res);  
  25.   
  26.             if (res_end < start)  
  27.                 continue;  
  28.             if (res_start < start)  
  29.                 res_start = start;  
  30.             if (res_start > end)  
  31.                 res_start = end;  
  32.             if (res_end > end)  
  33.                 res_end = end;  
  34.             if (res_start != start)  
  35.                 totalhigh_pages += free_area(start, res_start,  
  36.                                  NULL);  
  37.             start = res_end;  
  38.             if (start == end)  
  39.                 break;  
  40.         }  
  41.   
  42.         /* And now free anything which remains */  
  43.         if (start < end)  
  44.             totalhigh_pages += free_area(start, end, NULL);    //释放掉  
  45.     }  
  46.     totalram_pages += totalhigh_pages;  
  47. #endif  
  48. }</pre><br>  
  49. <br>  
  50. <pre></pre>  
  51. <p></p>  
  52. <pre></pre>  
  53. <pre name="code" class="cpp"><pre name="code" class="cpp">static inline int free_area(unsigned long pfn, unsigned long end, char *s)  
  54. {  
  55.     unsigned int pages = 0, size = (end - pfn) << (PAGE_SHIFT - 10);  
  56.   
  57.     for (; pfn < end; pfn++) {  
  58.         struct page *page = pfn_to_page(pfn);                     //释放对应的页  
  59.         ClearPageReserved(page);  
  60.         init_page_count(page);  
  61.         __free_page(page);  
  62.         pages++;  
  63.     }  
  64.   
  65.     if (size && s)  
  66.         printk(KERN_INFO "Freeing %s memory: %dK\n", s, size);  
  67.   
  68.     return pages;  
  69. }</pre><br>  
  70. <br>  
  71. <pre></pre>  
  72. <p></p>  
  73. <p>三、总结</p>  
  74. <p>        结合前面的分配释放,描述了从bootmem到buddy的过度,主要是bootmem时候内存的释放,以及高端内存的填充;</p>  
  75. <p>Thanks</p>  
  76. <p></p>  
  77. <p></p>  
  78. <p></p>  
  79. <pre></pre>  
  80. <pre></pre>  
  81. <pre></pre>  
  82. <pre></pre>  
  83. <pre></pre>  
  84.   
  85. </pre> 

原创粉丝点击