===================================
本文系本站原创,欢迎转载!
转载请注明出处: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