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

	
				
		
原创粉丝点击