分区页框分配器之水位
在讲分区页框分配器分配内存的时候,进入伙伴算法前用函数zone_watermark_fast(),来根据水位来判断当前内存情况。内存够的话采用伙伴算法分配,不够的话通过 node_reclaim 回收内存。
水位的初始化
先看下水位的初始化:
int __meminit init_per_zone_wmark_min(void)
{
unsigned long lowmem_kbytes;
int new_min_free_kbytes;
lowmem_kbytes = nr_free_buffer_pages() * (PAGE_SIZE >> 10); ------ (1)
new_min_free_kbytes = int_sqrt(lowmem_kbytes * 16);
if (new_min_free_kbytes > user_min_free_kbytes) {
min_free_kbytes = new_min_free_kbytes; ------ (2)
if (min_free_kbytes < 128)
min_free_kbytes = 128;
if (min_free_kbytes > 65536)
min_free_kbytes = 65536;
} else {
pr_warn("min_free_kbytes is not updated to %d because user defined value %d is preferred\n",
new_min_free_kbytes, user_min_free_kbytes);
}
setup_per_zone_wmarks(); ------ (3)
refresh_zone_stat_thresholds();
setup_per_zone_lowmem_reserve(); ------ (4)
#ifdef CONFIG_NUMA
setup_min_unmapped_ratio();
setup_min_slab_ratio();
#endif
return 0;
}
core_initcall(init_per_zone_wmark_min)
(1). nr_free_buffer_pages 是获取ZONE_DMA和ZONE_NORMAL区中高于high水位的总页数nr_free_buffer_pages = managed_pages - high_pages
(2). min_free_kbytes 是总的min大小,min_free_kbytes = 4 * sqrt(lowmem_kbytes)
(3). setup_per_zone_wmarks 根据总的min值,再加上各个zone在总内存中的占比,然后通过do_div就计算出他们各自的min值,进而计算出各个zone的水位大小。min,low,high的关系:low = min *125%,high = min * 150%。min,low,high之间的比例关系与 watermark_scale_factor 相关。可以通过 /proc/sys/vm/watermark_scale_factor 设置
(4). setup_per_zone_lowmem_reserve 设置每个zone的lowmem_reserve大小。lowmem_reserve值可以通过 /proc/sys/vm/lowmem_reserve_ratio 来修改。
为什么需要设置每个zone的保留内存呢,lowmem_reserve的作用是什么?
我们知道内核在分配内存时,会按照 HIGHMEM->NORMAL->DMA 的方向进行遍历,如果当前Zone分配失败,就会尝试下一个低级的Zone。我们可以想像应用进程通过内存映射申请 HIGHMEM,如果此时HIGHMEM Zone无法满足分配,则会尝试从 NORMAL 进行分配。这就有一个问题,来自 HIGHMEM Zone 的请求可能会耗尽 NORMAL Zone 的内存,最终的结果就是 NORMAL Zone 无内存提供给内核的正常分配。
因此针对这个场景,可以通过保留内存 lowmem_reserve[NORMAL] 给 NORMAL Zone 自己使用。
同样当从NORMAL Zone失败后,会尝试从zonelist中的DMA Zone申请,通过lowmem_reserve[DMA],限制来自HIGHMEM和NORMAL的分配请求。
$ cat /proc/sys/vm/lowmem_reserve_ratio
256 32
$ cat /proc/zoneinfo
Node 0, zone DMA32
......
pages free 361678
min 674
low 2874
high 3314
spanned 523776
present 496128
managed 440432
protection: (0, 3998, 3998)
......
Node 0, zone Normal
pages free 706981
min 1568
low 6681
high 7704
spanned 8912896
present 1048576
managed 1023570
protection: (0, 0, 0)
......
Node 0, zone Movable
pages free 0
min 0
low 0
high 0
spanned 0
present 0
managed 0
protection: (0, 0, 0)
- spanned:表示当前zone所包含的所有的pagespresent:表示当前zone在去掉第一阶段kernel reserve的内存之后剩下的pagesmanaged:表示当前zone去掉初始化完成以后所有的kernel reserve的内存剩下的pages
结合上面arm64平台的数值举个例子,假设这2个Zones分别包含440432, 1023570个pages(实际是/proc/zoneinfo里字段managed的值)。如下图所示,使用每个区域的 managed pages 和 lowmem_reserve_ratio 计算每个区域的lowmem_reserve值,可以看出结果和protection值一样。
水位的判断
从这张图可以看出:
- 如果空闲页数目min值,则该zone非常缺页,页面回收压力很大,应用程序写内存操作就会被阻塞,直接在应用程序的进程上下文中进行回收,即direct reclaim。如果空闲页数目小于low值,kswapd线程将被唤醒。默认情况下,low值为min值的125%,可以通过修改watermark_scale_factor来改变比例值如果空闲页面的值大于high值,kswapd线程将睡眠。
默认情况下,high值为min值的150%,可以通过修改watermark_scale_factor来改变比例值。