分享

linux内存管理分析【六】

 老匹夫 2014-09-12

        unsigned long flags;

        int migratetype;

        int wasMlocked = __TestClearPageMlocked(page);

        if (!free_pages_prepare(page, 0))

                return;

获取页的迁移类型

        migratetype = get_pageblock_migratetype(page);

将页的迁移类型保存到page->private中

        set_page_private(page, migratetype);

        local_irq_save(flags);

        if (unlikely(wasMlocked))

                free_page_mlock(page);

        __count_vm_event(PGFREE);

MIGRATE_PCPTYPES是每CPU缓存管理页帧类型的数目,如果某个页帧类型大于MIGRATE_PCPTYPES就将其挂到可移动列表中,如果迁移类型是MIGRATE_ISOLATE就直接将该页帧释放到伙伴系统中。

        if (migratetype >= MIGRATE_PCPTYPES) {

                if (unlikely(migratetype == MIGRATE_ISOLATE)) {

                        free_one_page(zone, page, 0, migratetype);

                        goto out;

                }

                migratetype = MIGRATE_MOVABLE;

        }

取得内存域的每CPU管理结构

        pcp = &this_cpu_ptr(zone->pageset)->pcp;

Cold标示冷页或热页,如果是冷页就将页帧挂到对应迁移类型链表的末尾,如果是热页就将页帧挂到对应迁移类型链表的头部

       if (cold)

                list_add_tail(&page->lru, &pcp->lists[migratetype]);

        else

                list_add(&page->lru, &pcp->lists[migratetype]);

        pcp->count++;

每CPU缓存有一个最大页帧的阀值,如果每CPU缓存中页帧数大于这个阀值,就将一批(pcp->batch)页帧释到伙伴系统中。

        if (pcp->count >= pcp->high) {

                free_pcppages_bulk(zone, pcp->batch, pcp);

                pcp->count -= pcp->batch;

        }

out:

        local_irq_restore(flags);

}

【__free_pages--->free_hot_cold_page--->free_pcppages_bulk】

static void free_pcppages_bulk(struct zone *zone, int count,struct per_cpu_pages *pcp)

{

        int migratetype = 0;

        int batch_free = 0;

        int to_free = count;

        spin_lock(&zone->lock);

all_unreclaimable表示内存紧张程度,释放内存后将此标志清除

        zone->all_unreclaimable = 0;

pages_scanned最后一次内存紧张以来,页面回收过程已经扫描的页数。现在正在释放类存,将此值清零,待回收时重新计数

        zone->pages_scanned = 0;

循环将一批每CPU缓存中页帧释放到伙伴系统中去

        while (to_free) {

                struct page *page;

                struct list_head *list;

                do {

在每CPU管理结构中取出一个不为空的列表,batch_free++表示迁移类型越靠后的链表中释放的页越多。

                        batch_free++;

                        if (++migratetype == MIGRATE_PCPTYPES)

                                migratetype = 0;

                        list = &pcp->lists[migratetype];

                } while (list_empty(list));

                if (batch_free == MIGRATE_PCPTYPES)

                        batch_free = to_free;

                do {

将页帧从链表中取下,然后释放

                        page = list_entry(list->prev, struct page, lru);

                        list_del(&page->lru);

                        __free_one_page(page, zone, 0, page_private(page));

                        trace_mm_page_pcpu_drain(page, 0, page_private(page));

                } while (--to_free && --batch_free && !list_empty(list));

        }

        __mod_zone_page_state(zone, NR_FREE_PAGES, count);

        spin_unlock(&zone->lock);

}

【__free_pages--->free_hot_cold_page--->free_pcppages_bulk--->__free_one_page】

mm/page_alloc.c

static inline void __free_one_page(struct page *page,

                struct zone *zone, unsigned int order,

                int migratetype)

{

        unsigned long page_idx;

        unsigned long combined_idx;

        unsigned long uninitialized_var(buddy_idx);

        struct page *buddy;

复合页的第一个页称作首页,其余所有各页称作尾页。组成复合页的所有页的page实例的private成员,包括首页在内,都指向首页。如果要释放的页是复合页就清除复合页的一些标志,让其变为普通页然后释放。

        if (unlikely(PageCompound(page)))

                if (unlikely(destroy_compound_page(page, order)))

                        return;

        VM_BUG_ON(migratetype == -1);

伙伴系统中,页帧的周期为2^MAX_ORDER,page_idx是周期内的偏移

        page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1);

        VM_BUG_ON(page_idx & ((1 << order) - 1));

        VM_BUG_ON(bad_range(zone, page));

遍历order以上所有阶,将页帧依次向上合并

        while (order < MAX_ORDER-1) {

函数__find_buddy_index就一行代码page_idx ^ (1 << order),将page_idx的1 << order位取反。

                buddy_idx = __find_buddy_index(page_idx, order);

如果page_idx的1 << order位为1,buddy_idx 与page_idx相减的结果就是负的(1 << order);如果如果page_idx的1 << order位为0,相减的结果就为正的(1 << order)。Buddy是page的伙伴页。以当前页帧实例page为基准,向左或向右偏移(1 << order)。

                buddy = page + (buddy_idx - page_idx);

函数page_is_buddy主要做四项检查:

1,伙伴页是否处于一个空洞中

2,伙伴页是否在伙伴系统中

3,page和它的伙伴页是否有相同的阶

4,page和它的伙伴页是否处于相同的内存域中

                if (!page_is_buddy(page, buddy, order))

                        break;

             

                if (page_is_guard(buddy)) {

                        clear_page_guard_flag(buddy);

                        set_page_private(page, 0);

                        __mod_zone_page_state(zone, NR_FREE_PAGES, 1 << order);

                } else {

将伙伴页从列表中删除,因为它将和其它页合并,然后将首页挂到更高阶的列表中。

                        list_del(&buddy->lru);

递减该阶中的页计数

                        zone->free_area[order].nr_free--;

清除页的“在伙伴系统中”的标志,然后将页块的阶数page->private置为0

                        rmv_page_order(buddy);

                }

将page_idx的1 << order位清零

                combined_idx = buddy_idx & page_idx;

求order+1阶页块的首页

                page = page + (combined_idx - page_idx);

                page_idx = combined_idx; 更新page_idx

                order++;阶数递增

        }

设置首页的阶数,将首页标记为伙伴系统页

        set_page_order(page, order);

在order阶向order+1阶的页块合并中失败了,下面是判断如果下次order阶向order+1阶的页块合并能够成功的话,就将order阶页块链接到列表的末尾,避免下次被分配出去,以便以后合并为更高阶的页块。

        if ((order < MAX_ORDER-2) && pfn_valid_within(page_to_pfn(buddy)))  {

                struct page *higher_page, *higher_buddy;

求高一阶的页块首页

                combined_idx = buddy_idx & page_idx;

                higher_page = page + (combined_idx - page_idx);

找出高一阶页块的伙伴页

                buddy_idx = __find_buddy_index(combined_idx, order + 1);

                higher_buddy = page + (buddy_idx - combined_idx);

检查高一阶页是否可以合并,如果可以合并就将order阶页链接到列表尾

                if (page_is_buddy(higher_page, higher_buddy, order + 1)) {

                        list_add_tail(&page->lru,

                             &zone->free_area[order].free_list[migratetype]);

                        goto out;

                }

        }

如果高一阶页不可合并就将页块链接到列表首

        list_add(&page->lru, &zone->free_area[order].free_list[migratetype]);

out:

增加对应阶的空闲页计数

        zone->free_area[order].nr_free++;

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

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多