Linux页框分配函数的实现(2)-慢速内存分配

2012年2月28日 由 edsionte 留言 »

2. 慢速分配函数

进入慢速分配函数后,先检查所请求的分配阶是否超过了MAX_ORDER。如果指定了GFP_THISNODE标志后,则不能继续进行慢速内存分配,因为该标志指明了内存不能进行回收,因此直接跳到nopage处的代码。

在经历一系列的参数检查之后,该函数通过调用wake_all_kswapd()唤醒每个zone所属node中的kswapd守护进程。这个守护进程负责换出很少使用的页,以提高目前系统可以用的空闲页框。

在kswapd交换进程被唤醒之后,该函数开始尝试新一轮的分配。它首先通过gfp_to_alloc_flags()对分配标志进行调整,稍微降低分配标准以便这次调用get_page_from_freelist()有可能分配到内存。

static inline struct page *
__alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
        struct zonelist *zonelist, enum zone_type high_zoneidx,
        nodemask_t *nodemask, struct zone *preferred_zone,
        int migratetype)
{
        const gfp_t wait = gfp_mask & __GFP_WAIT;
        struct page *page = NULL;
        int alloc_flags;
        unsigned long pages_reclaimed = 0;
        unsigned long did_some_progress;
        struct task_struct *p = current;

        if (order >= MAX_ORDER) {
                WARN_ON_ONCE(!(gfp_mask & __GFP_NOWARN));
                return NULL;
        }

        if (NUMA_BUILD && (gfp_mask & GFP_THISNODE) == GFP_THISNODE)
                goto nopage;

restart:
        wake_all_kswapd(order, zonelist, high_zoneidx);
        alloc_flags = gfp_to_alloc_flags(gfp_mask);
        page = get_page_from_freelist(gfp_mask, nodemask, order, zonelist,
                        high_zoneidx, alloc_flags & ~ALLOC_NO_WATERMARKS,
                        preferred_zone, migratetype);
        if (page)
                goto got_pg;

如果page不为空,则说明内存申请成功,否则继续进行慢速内存分配。如果设置了ALLOC_NO_WATERMARKS标志,那么此时会忽略水印,并此时进入__alloc_pages_high_priority()。该函数内部会至少会再调用一次get_page_from_freelist(),如果设置了__GFP_NOFAIL标志,则不断的循环等待并尝试进行内存分配。

rebalance:
        if (alloc_flags & ALLOC_NO_WATERMARKS) {
                page = __alloc_pages_high_priority(gfp_mask, order,
                                zonelist, high_zoneidx, nodemask,
                                preferred_zone, migratetype);
                if (page)
                        goto got_pg;
        }

如果没有设置__GFP_WAIT,即wait为0,则不继续进行内存分配,直接跳到nopage处。如果PF_MEMALLOC被设置,也就是说调用内存分配函数着本身就是内存回收进程,则直接跳到nopage处。

        if (!wait)
                goto nopage;

        if (p->flags & PF_MEMALLOC)
                goto nopage;

        if (test_thread_flag(TIF_MEMDIE) && !(gfp_mask & __GFP_NOFAIL))
                goto nopage;

到目前为止,分配函数已经尝试好几次页框分配。如果现在仍未分配到请求的内存,那么接下来将进入一个比较耗时的阶段。内核通过将很少使用的页换出到磁盘上,以便在物理内存中有更多的空闲页框。这个过程可能会产生阻塞,也就是说会产生睡眠,因此它比较耗时。

__alloc_pages_direct_reclaim()的作用就是先通过try_to_free_pages()回收一些最近很少用的页,将其写回磁盘上的交换区,以便在物理内存中腾出更多的空间。接着,内核会再次调用get_page_from_freelist()尝试分配内存。

        page = __alloc_pages_direct_reclaim(gfp_mask, order,
                                        zonelist, high_zoneidx,
                                        nodemask,
                                        alloc_flags, preferred_zone,
                                        migratetype, &did_some_progress);
        if (page)
                goto got_pg;

如果内核进行了上述的回收和重新分配的过程后,仍未分配成功,既did_some_progress为0,那么此时内核不的不考虑是否发生了OOM(out of memory)。如果当前请求内存的进程发生了OOM,也就是说该进程试图拥有过多的内存,那么此时内核会调用OOM killer杀死它。并且跳转到restart处,重新进行内存分配。

        if (!did_some_progress) {
                if ((gfp_mask & __GFP_FS) && !(gfp_mask & __GFP_NORETRY)) {
                        if (oom_killer_disabled)
                                goto nopage;
                        page = __alloc_pages_may_oom(gfp_mask, order,
                                        zonelist, high_zoneidx,
                                        nodemask, preferred_zone,
                                        migratetype);
                        if (page)
                                goto got_pg;

                        if (order > PAGE_ALLOC_COSTLY_ORDER &&
                                                !(gfp_mask & __GFP_NOFAIL))
                                goto nopage;

                        goto restart;
                }
        }

此时再次判断是否要重新进行一次内存申请。如果有这个必要,那么等待写操作完成后再次跳到rebalance处重试。

        pages_reclaimed += did_some_progress;
        if (should_alloc_retry(gfp_mask, order, pages_reclaimed)) {
                congestion_wait(BLK_RW_ASYNC, HZ/50);
                goto rebalance;
        }

页框分配函数结束时候一般有两种情况,其中之一即为分配失败,并没有得到所需页框,因此打印一些内存分配失败的信息。

nopage:
        if (!(gfp_mask & __GFP_NOWARN) && printk_ratelimit()) {
                printk(KERN_WARNING "%s: page allocation failure."
                        " order:%d, mode:0x%x\n",
                        p->comm, order, gfp_mask);
                dump_stack();
                show_mem();
        }
        return page;

另一种情况,也就是得到了所需页框,那么直接返回这组页框的首页框描述符。

got_pg:
        if (kmemcheck_enabled)
                kmemcheck_pagealloc_alloc(page, order, gfp_mask);
        return page;

}

通过上述的过程可以看到,页框分配函数__alloc_pages()会多次尝试进行分配内存。而具体的页框分配操作是在get_page_from_freelist()中完成的,它根据伙伴算法分配所需大小的页框。

广告位

发表回复

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key