linux 页框大小设置,Linux页框分配函数的实现(2)-慢速内存分配

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()中完成的,它根据伙伴算法分配所需大小的页框。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值