Linux 內存管理的水位控制
本文轉載自微信公眾號「人人都是極客」,作者布道師Peter 。轉載本文請聯系人人都是極客公眾號。
分區頁框分配器之水位
在講分區頁框分配器分配內存的時候,進入伙伴算法前用函數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所包含的所有的pages
- present:表示當前zone在去掉第一階段kernel reserve的內存之后剩下的pages
- managed:表示當前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來改變比例值