分享

linux内存管理分析【五】

 Vin的藏書閣 2013-04-01
伙伴系统

6.1 伙伴系统数据结构

通过伙伴系统分配内存页的过程就是:在NUMA系统中找到指定的结点,在节点中找到合适的内存域,在找到的内存域中找到指定的阶,在指定的分配阶中找到指定的迁移类型,最后在合适的迁移类型中进行内存分配。

include/linux/mmzone.h

typedef struct pglist_data {

        struct zone node_zones[MAX_NR_ZONES];本结点内存域

MAX_ZONELISTSNUMA中的值是2,在UMA系统中值是1node_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); 将页释放到伙伴系统中

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多