6 伙伴系统 6.1 伙伴系统数据结构 通过伙伴系统分配内存页的过程就是:在NUMA系统中找到指定的结点,在节点中找到合适的内存域,在找到的内存域中找到指定的阶,在指定的分配阶中找到指定的迁移类型,最后在合适的迁移类型中进行内存分配。 include/linux/mmzone.h typedef struct pglist_data { struct zone node_zones[MAX_NR_ZONES];本结点内存域 MAX_ZONELISTS在NUMA中的值是2,在UMA系统中值是1。node_zonelists[0]中是本结点按分配成本排列的内存域,node_zonelists[0]是系统中其他结点的内存域。 struct zonelist node_zonelists[MAX_ZONELISTS]; int nr_zones; 本结点内存域数目 ...... } 每个内存域的空闲内存页都链接在struct free_area对应的阶中。 include/linux/mmzone.h struct zone { ...... struct free_area free_area[MAX_ORDER]; ...... } 每一个阶的内存块由一个struct free_area结构来管理 include/linux/mmzone.h #ifndef CONFIG_FORCE_MAX_ZONEORDER #define MAX_ORDER 11 #else #define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER #endif 每一个阶的内存块又有不同的迁移类型,每一个迁移类型对应一个链表。 include/linux/mmzone.h struct free_area { struct list_head free_list[MIGRATE_TYPES]; unsigned long nr_free; 内存块的数目 };
借用一张图来展示伙伴系统内存结构:
6.2 伙伴系统的建立
【bootmem_init--->arm_bootmem_free--->free_area_init_node--->free_area_init_core】 初始化每个内存域的内存阶链表 static void __paginginit free_area_init_core(struct pglist_data *pgdat, unsigned long *zones_size, unsigned long *zholes_size) { ...... for (j = 0; j < MAX_NR_ZONES; j++) { ...... ret = init_currently_empty_zone(zone, zone_start_pfn, size, MEMMAP_EARLY); ...... } }
__meminit int init_currently_empty_zone(struct zone *zone, unsigned long zone_start_pfn, unsigned long size, enum memmap_context context) { ...... pgdat->nr_zones = zone_idx(zone) + 1; zone->zone_start_pfn = zone_start_pfn;内存域起始页帧 ...... zone_init_free_lists(zone); return 0; }
static void __meminit zone_init_free_lists(struct zone *zone) { int order, t; for_each_migratetype_order(order, t) { INIT_LIST_HEAD(&zone->free_area[order].free_list[t]); zone->free_area[order].nr_free = 0; } }
宏for_each_migratetype_order(order, t)是一个双循环,遍历内存域的每一个阶和阶的每一种迁移类型。 include/linux/mmzone.h #define for_each_migratetype_order(order, type) \ for (order = 0; order < MAX_ORDER; order++) \ for (type = 0; type < MIGRATE_TYPES; type++)
【start_kernel--->mm_init--->mem_init--->free_all_bootmem】
unsigned long __init free_all_bootmem(void) { unsigned long total_pages = 0; bootmem_data_t *bdata; 每一个结点的自举分配管理结构都注册在链表bdata_list中。下面循环遍历每一个自举分配管理结构,并将该结点中的空闲内存释放到伙伴系统中去。 list_for_each_entry(bdata, &bdata_list, list) total_pages += free_all_bootmem_core(bdata);
return total_pages; }
static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) { struct page *page; unsigned long start, end, pages, count = 0;
if (!bdata->node_bootmem_map) return 0; 获取该结点中的起始页帧和结束页帧 start = bdata->node_min_pfn; end = bdata->node_low_pfn; 遍历结点中的每一个页帧,并将其释放到伙伴系统中去 while (start < end) { unsigned long *map, idx, vec; 页帧对应位表,置1的位表示对应页已分配,为0的位表示对应页空闲 map = bdata->node_bootmem_map; idx = start - bdata->node_min_pfn;计算页帧偏移 根据页帧偏移计算出页帧在位表中对应的整形存储区域,取反表示为1的位对应页为空闲,为0的位为已分配 vec = ~map[idx / BITS_PER_LONG]; 如果位表某个整形存储区所有位都是1,就将其32个页帧作为一个块释放到伙伴系统中 if (IS_ALIGNED(start, BITS_PER_LONG) && vec == ~0UL) { int order = ilog2(BITS_PER_LONG); __free_pages_bootmem(pfn_to_page(start), order); count += BITS_PER_LONG; start += BITS_PER_LONG; } else { unsigned long off = 0; 将整形存储区中为1的位对应的页帧释放到伙伴系统中 vec >>= start & (BITS_PER_LONG - 1); while (vec) { if (vec & 1) { page = pfn_to_page(start + off); __free_pages_bootmem(page, 0); count++; } vec >>= 1; off++; } start = ALIGN(start + 1, BITS_PER_LONG); } } 将结点中位表所占用的存储空间释放到伙伴系统中 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); return count; }
void __meminit __free_pages_bootmem(struct page *page, unsigned int order) { unsigned int nr_pages = 1 << order; unsigned int loop;
prefetchw(page); for (loop = 0; loop < nr_pages; loop++) { struct page *p = &page[loop];
if (loop + 1 < nr_pages) prefetchw(p + 1); 在函数bootmem_init--->arm_bootmem_free--->free_area_init_node---> free_area_init_core--->memmap_init中有SetPageReserved(page);。由于在那时使用自举分配器分配内存页,page结构并没有使用,其分配函数memblock_alloc_base()返回的是页地址,而不是page结构地址。 __ClearPageReserved(p); set_page_count(p, 0); }
set_page_refcounted(page); __free_pages(page, order); 将页释放到伙伴系统中 |
|
来自: Vin的藏書閣 > 《linux内存管理分析》