分區(qū)頁(yè)框分配器之水位
在講分區(qū)頁(yè)框分配器分配內(nèi)存的時(shí)候,進(jìn)入伙伴算法前用函數(shù)zone_watermark_fast(),來(lái)根據(jù)水位來(lái)判斷當(dāng)前內(nèi)存情況。內(nèi)存夠的話采用伙伴算法分配,不夠的話通過(guò) node_reclaim 回收內(nèi)存。
水位的初始化
先看下水位的初始化:
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 preferredn",
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區(qū)中高于high水位的總頁(yè)數(shù)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 根據(jù)總的min值,再加上各個(gè)zone在總內(nèi)存中的占比,然后通過(guò)do_div就計(jì)算出他們各自的min值,進(jìn)而計(jì)算出各個(gè)zone的水位大小。min,low,high的關(guān)系:low = min *125%,high = min * 150%。min,low,high之間的比例關(guān)系與 watermark_scale_factor 相關(guān)??梢酝ㄟ^(guò) /proc/sys/vm/watermark_scale_factor 設(shè)置
(4). setup_per_zone_lowmem_reserve 設(shè)置每個(gè)zone的lowmem_reserve大小。lowmem_reserve值可以通過(guò) /proc/sys/vm/lowmem_reserve_ratio 來(lái)修改。
為什么需要設(shè)置每個(gè)zone的保留內(nèi)存呢,lowmem_reserve的作用是什么?
我們知道內(nèi)核在分配內(nèi)存時(shí),會(huì)按照 HIGHMEM->NORMAL->DMA 的方向進(jìn)行遍歷,如果當(dāng)前Zone分配失敗,就會(huì)嘗試下一個(gè)低級(jí)的Zone。我們可以想像應(yīng)用進(jìn)程通過(guò)內(nèi)存映射申請(qǐng) HIGHMEM,如果此時(shí)HIGHMEM Zone無(wú)法滿足分配,則會(huì)嘗試從 NORMAL 進(jìn)行分配。這就有一個(gè)問(wèn)題,來(lái)自 HIGHMEM Zone 的請(qǐng)求可能會(huì)耗盡 NORMAL Zone 的內(nèi)存,最終的結(jié)果就是 NORMAL Zone 無(wú)內(nèi)存提供給內(nèi)核的正常分配。
因此針對(duì)這個(gè)場(chǎng)景,可以通過(guò)保留內(nèi)存 lowmem_reserve[NORMAL] 給 NORMAL Zone 自己使用。
同樣當(dāng)從NORMAL Zone失敗后,會(huì)嘗試從zonelist中的DMA Zone申請(qǐng),通過(guò)lowmem_reserve[DMA],限制來(lái)自HIGHMEM和NORMAL的分配請(qǐng)求。
$ 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:表示當(dāng)前zone所包含的所有的pagespresent:表示當(dāng)前zone在去掉第一階段kernel reserve的內(nèi)存之后剩下的pagesmanaged:表示當(dāng)前zone去掉初始化完成以后所有的kernel reserve的內(nèi)存剩下的pages
結(jié)合上面arm64平臺(tái)的數(shù)值舉個(gè)例子,假設(shè)這2個(gè)Zones分別包含440432, 1023570個(gè)pages(實(shí)際是/proc/zoneinfo里字段managed的值)。如下圖所示,使用每個(gè)區(qū)域的 managed pages 和 lowmem_reserve_ratio 計(jì)算每個(gè)區(qū)域的lowmem_reserve值,可以看出結(jié)果和protection值一樣。
水位的判斷
從這張圖可以看出:
- 如果空閑頁(yè)數(shù)目min值,則該zone非常缺頁(yè),頁(yè)面回收壓力很大,應(yīng)用程序寫內(nèi)存操作就會(huì)被阻塞,直接在應(yīng)用程序的進(jìn)程上下文中進(jìn)行回收,即direct reclaim。如果空閑頁(yè)數(shù)目小于low值,kswapd線程將被喚醒。默認(rèn)情況下,low值為min值的125%,可以通過(guò)修改watermark_scale_factor來(lái)改變比例值如果空閑頁(yè)面的值大于high值,kswapd線程將睡眠。
默認(rèn)情況下,high值為min值的150%,可以通過(guò)修改watermark_scale_factor來(lái)改變比例值。