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); 将页释放到伙伴系统中
联系客服