内存回收主要是有kswapd异步回收和direct reclaim同步回收两种入口,其中逻辑非常复杂,本文主要只概要描述不同回收场景下内核设计的主要思想,源码细节不同版本有不少区别,具体的分析后续会有专门的文章分析。
页面回收常识:
1)何时唤起kswapd:
2)kswapd何时停止,回收(一个for循环)的频率如何
核心思路:既要高效回收page,又不能过度消耗CPU。
3)kswapd 回收page目标数量是多少?
for (z = 0; z <= sc->reclaim_idx; z++) {
zone = pgdat->node_zones + z;
if (!managed_zone(zone))
continue;
//high_wmark_pages返回的high watermark值
sc->nr_to_reclaim += max(high_wmark_pages(zone), SWAP_CLUSTER_MAX);
}
注意:虽然kswapd的sc.nr_to_reclaim虽然设置的回收目标挺大的,但是真正执行shrink回收页面的时候(实际上指shrink_list)一次回收的单位最大是SWAP_CLUSTER_MAX(32)个pages。
4)kswapd回收的方向(zone)和 order?
5)kswapd's scan control priority优先级设置
static int __node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask, unsigned int order)
{
...
struct scan_control sc = {
.nr_to_reclaim = max(nr_pages, SWAP_CLUSTER_MAX),
.gfp_mask = current_gfp_context(gfp_mask),
.order = order,
.priority = NODE_RECLAIM_PRIORITY,
.may_writepage = !!(node_reclaim_mode & RECLAIM_WRITE),
.may_unmap = !!(node_reclaim_mode & RECLAIM_UNMAP),
.may_swap = 1,
.reclaim_idx = gfp_zone(gfp_mask),
};
...
}
my_writepage = 0:代表不能进行页面回写落盘
my_unmap = 0 : 不能进行try_to_unmap(page)等unmap解除页表映射。
may_swap = 1;可以进行页面swap操作,android上比如可以机型zram匿名页压缩。
priority : 回收优先级是4,即最多尝试调用shrink_node进行回收的次数为4次,或者当回收到的页数达到需要分配的内存页数停止。
nr_to_reclaim:请求分配页面和SWAP_CLUSTER_MAX的最大值,即最少也要扫描32个page,否则一次shrink性价比太低了。
慢路径申请:
如果快速路径回收页面后依旧分配内存失败,则会进入到慢速分配路径,慢速分配会尝试从所有zone中以WMARK_MIN阀值分配内存页。在慢速分配路径中,会再次尝试快速路径回收,未成功则调用__alloc_pages_direct_reclaim进行内存回收。允许mapped anonymous page回收,且如果laptop_mode不为真,则允许脏文件页写回。laptop_mode通过/proc/sys/vm/laptop_mode控制,为0允许直接内存回收对脏页进行回写操作。try_to_free_pages每次回收页面最大值为32(SWAP_CLUSTER_MAX),priority为12,shrink_node最多会被调用12次。
unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
gfp_t gfp_mask, nodemask_t *nodemask)
{
unsigned long nr_reclaimed;
struct scan_control sc = {
.nr_to_reclaim = SWAP_CLUSTER_MAX,
.gfp_mask = current_gfp_context(gfp_mask),
.reclaim_idx = gfp_zone(gfp_mask),
.order = order,
.nodemask = nodemask,
.priority = DEF_PRIORITY,
.may_writepage = !laptop_mode,
.may_unmap = 1,
.may_swap = 1,
};
调用栈:
#0 shrink_node (pgdat=0xffff888007fda000, sc=0xffff888005767450) at mm/vmscan.c:2672
#1 0xffffffff81356a4a in shrink_zones (sc=, zonelist=) at mm/vmscan.c:2971
#2 do_try_to_free_pages (zonelist=, sc=0xffff888005767450) at mm/vmscan.c:3026
#3 0xffffffff81358cec in try_to_free_pages (zonelist=0xffff888007fdb400, order=0, gfp_mask=, nodemask=) at mm/vmscan.c:3265
#4 0xffffffff813bcd27 in __perform_reclaim (ac=, ac=, order=, gfp_mask=) at mm/page_alloc.c:4274
#5 __alloc_pages_direct_reclaim (did_some_progress=, ac=, alloc_flags=, order=, gfp_mask=) at mm/page_alloc.c:4295
#6 __alloc_pages_slowpath (gfp_mask=, order=, ac=0x0 ) at mm/page_alloc.c:4699
#7 0xffffffff813bdf9d in __alloc_pages_nodemask (gfp_mask=1125578, order=0, preferred_nid=, nodemask=) at mm/page_alloc.c:4915
#8 0xffffffff813e0637 in alloc_pages_current (gfp=1125578, order=0) at ./include/linux/topology.h:88
#9 0xffffffff8132b0b5 in alloc_pages (order=, gfp_mask=) at ./include/linux/gfp.h:545
#10 __page_cache_alloc (gfp=) at mm/filemap.c:957
#11 __page_cache_alloc (gfp=1125578) at mm/filemap.c:942
具体回收的过程以及判定哪些页面怎么处理的细节后面文章分析。
现在内核如果kswapd回收,但是一直无法balance,没有措施确保kswapd能歇一歇?会不会造成cpu浪费?