溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何理解kswapd的低水位min_free_kbytes

發(fā)布時(shí)間:2021-11-08 18:28:13 來(lái)源:億速云 閱讀:219 作者:柒染 欄目:建站服務(wù)器

這篇文章給大家介紹如何理解kswapd的低水位min_free_kbytes,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

1. min_free_kbytes

先看官方解釋:
This is used to force the Linux VM to keep a minimum number of kilobytes free. The VM uses this number to compute a watermark[WMARK_MIN] value for each lowmem zone in the system. Each lowmem zone gets a number of reserved free pages based proportionally on its size.
Some minimal amount of memory is needed to satisfy PF_MEMALLOC allocations; if you set this to lower than 1024KB, your system will become subtly broken, and prone to deadlock under high loads.
Setting this too high will OOM your machine instantly.
解釋已經(jīng)很清楚了,主要有以下幾個(gè)關(guān)鍵點(diǎn):

1. 代表系統(tǒng)所保留空閑內(nèi)存的最低限。

在系統(tǒng)初始化時(shí)會(huì)根據(jù)內(nèi)存大小計(jì)算一個(gè)默認(rèn)值,計(jì)算規(guī)則是:
min_free_kbytes = sqrt(lowmem_kbytes * 16) = 4 * sqrt(lowmem_kbytes)(注:lowmem_kbytes即可認(rèn)為是系統(tǒng)內(nèi)存大?。?br/>另外,計(jì)算出來(lái)的值有最小最大限制,最小為128K,最大為64M。
可以看出,min_free_kbytes隨著內(nèi)存的增大不是線性增長(zhǎng),comments里提到了原因“because network bandwidth does not increase linearly with machine size”。隨著內(nèi)存的增大,沒有必要也線性的預(yù)留出過多的內(nèi)存,能保證緊急時(shí)刻的使用量便足矣。

2.min_free_kbytes的主要用途是計(jì)算影響內(nèi)存回收的三個(gè)參數(shù) watermark[min/low/high]

1) watermark[high] > watermark [low] > watermark[min],各個(gè)zone各一套

2)在系統(tǒng)空閑內(nèi)存低于 watermark[low]時(shí),開始啟動(dòng)內(nèi)核線程kswapd進(jìn)行內(nèi)存回收(每個(gè)zone一個(gè)),直到該zone的空閑內(nèi)存數(shù)量達(dá)到watermark[high]后停止回收。如果上層申請(qǐng)內(nèi)存的速度太快,導(dǎo)致空閑內(nèi)存降至watermark[min]后,內(nèi)核就會(huì)進(jìn)行direct reclaim(直接回收),即直接在應(yīng)用程序的進(jìn)程上下文中進(jìn)行回收,再用回收上來(lái)的空閑頁(yè)滿足內(nèi)存申請(qǐng),因此實(shí)際會(huì)阻塞應(yīng)用程序,帶來(lái)一定的響應(yīng)延遲,而且可能會(huì)觸發(fā)系統(tǒng)OOM。這是因?yàn)閣atermark[min]以下的內(nèi)存屬于系統(tǒng)的自留內(nèi)存,用以滿足特殊使用,所以不會(huì)給用戶態(tài)的普通申請(qǐng)來(lái)用。

3)三個(gè)watermark的計(jì)算方法:
watermark[min] = min_free_kbytes換算為page單位即可,假設(shè)為min_free_pages。(因?yàn)槭敲總€(gè)zone各有一套watermark參數(shù),實(shí)際計(jì)算效果是根據(jù)各個(gè)zone大小所占內(nèi)存總大小的比例,而算出來(lái)的per zone min_free_pages)
watermark[low] = watermark[min] * 5 / 4
watermark[high] = watermark[min] * 3 / 2
所以中間的buffer量為 high - low = low - min = per_zone_min_free_pages * 1/4。因?yàn)閙in_free_kbytes = 4* sqrt(lowmem_kbytes),也可以看出中間的buffer量也是跟內(nèi)存的增長(zhǎng)速度成開方關(guān)系。

4)可以通過/proc/zoneinfo查看每個(gè)zone的watermark
例如:

Node 0, zone      DMA
pages free     3960
       min      65
       low      81
       high     97

3.min_free_kbytes大小的影響
min_free_kbytes設(shè)的越大,watermark的線越高,同時(shí)三個(gè)線之間的buffer量也相應(yīng)會(huì)增加。這意味著會(huì)較早的啟動(dòng)kswapd進(jìn)行回收,且會(huì)回收上來(lái)較多的內(nèi)存(直至watermark[high]才會(huì)停止),這會(huì)使得系統(tǒng)預(yù)留過多的空閑內(nèi)存,從而在一定程度上降低了應(yīng)用程序可使用的內(nèi)存量。極端情況下設(shè)置min_free_kbytes接近內(nèi)存大小時(shí),留給應(yīng)用程序的內(nèi)存就會(huì)太少而可能會(huì)頻繁地導(dǎo)致OOM的發(fā)生。
min_free_kbytes設(shè)的過小,則會(huì)導(dǎo)致系統(tǒng)預(yù)留內(nèi)存過小。kswapd回收的過程中也會(huì)有少量的內(nèi)存分配行為(會(huì)設(shè)上PF_MEMALLOC)標(biāo)志,這個(gè)標(biāo)志會(huì)允許kswapd使用預(yù)留內(nèi)存;另外一種情況是被OOM選中殺死的進(jìn)程在退出過程中,如果需要申請(qǐng)內(nèi)存也可以使用預(yù)留部分。這兩種情況下讓他們使用預(yù)留內(nèi)存可以避免系統(tǒng)進(jìn)入deadlock狀態(tài)。

2. lowmem_reserve_ratio

官方解釋:

For some specialised workloads on highmem machines it is dangerous for the kernel to allow process memory to be allocated from the "lowmem" zone. This is because that memory could then be pinned via the mlock() system call, or by unavailability of swapspace.
And on large highmem machines this lack of reclaimable lowmem memory can be fatal.
So the Linux page allocator has a mechanism which prevents allocations which could use highmem from using too much lowmem. This means that a certain amount of lowmem is defended from the possibility of being captured into pinned user memory.
The `lowmem_reserve_ratio' tunable determines how aggressive the kernel is in defending these lower zones.
If you have a machine which uses highmem or ISA DMA and your applications are using mlock(), or if you are running with no swap then you probably should change the lowmem_reserve_ratio setting.

1.作用
除了min_free_kbytes會(huì)在每個(gè)zone上預(yù)留一部分內(nèi)存外,lowmem_reserve_ratio是在各個(gè)zone之間進(jìn)行一定的防衛(wèi)預(yù)留,主要是防止高端zone在沒內(nèi)存的情況下過度使用低端zone的內(nèi)存資源。
例如現(xiàn)在常見的一個(gè)node的機(jī)器有三個(gè)zone: DMA,DMA32和NORMAL。DMA和DMA32屬于低端zone,內(nèi)存也較小,如96G內(nèi)存的機(jī)器兩個(gè)zone總和才1G左右,NORMAL就相對(duì)屬于高端內(nèi)存(現(xiàn)在一般沒有HIGH zone),而且數(shù)量較大(>90G)。低端內(nèi)存有一定的特殊作用比如發(fā)生DMA時(shí)只能分配DMA zone的低端內(nèi)存,因此需要在 盡量可以使用高端內(nèi)存時(shí) 而 不使用低端內(nèi)存,同時(shí)防止高端內(nèi)存分配不足的時(shí)候搶占稀有的低端內(nèi)存。

  1. 計(jì)算方法

cat /proc/sys/vm/lowmem_reserve_ratio256     256     32

內(nèi)核利用上述的protection數(shù)組計(jì)算每個(gè)zone的預(yù)留page量,計(jì)算出來(lái)也是數(shù)組形式,從/proc/zoneinfo里可以查看:

Node 0, zone      DMA
 pages free     1355
       min      3
       low      3
       high     4
       :
       :
   numa_other   0       protection: (0, 2004, 2004, 2004)
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 pagesets   cpu: 0 pcp: 0
       :

在進(jìn)行內(nèi)存分配時(shí),這些預(yù)留頁(yè)數(shù)值和watermark相加來(lái)一起決定現(xiàn)在是滿足分配請(qǐng)求,還是認(rèn)為空閑內(nèi)存量過低需要啟動(dòng)回收。
例如,如果一個(gè)normal區(qū)(index = 2)的頁(yè)申請(qǐng)來(lái)試圖分配DMA區(qū)的內(nèi)存,且現(xiàn)在使用的判斷標(biāo)準(zhǔn)是watermark[low]時(shí),內(nèi)核計(jì)算出 page_free = 1355,而watermark + protection[2] = 3 + 2004 = 2007 > page_free,則認(rèn)為空閑內(nèi)存太少而不予以分配。如果分配請(qǐng)求本就來(lái)自DMA zone,則 protection[0] = 0會(huì)被使用,而滿足分配申請(qǐng)。
zone[i] 的 protection[j] 計(jì)算規(guī)則如下:

(i < j):
 zone[i]->protection[j]
 = (total sums of present_pages from zone[i+1] to zone[j] on the node)
   / lowmem_reserve_ratio[i];
(i = j):
  (should not be protected. = 0;
(i > j):
  (not necessary, but looks 0)

默認(rèn)的 lowmem_reserve_ratio[i] 值是:

   256 (if zone[i] means DMA or DMA32 zone)   32  (others).

從上面的計(jì)算規(guī)則可以看出,預(yù)留內(nèi)存值是ratio的倒數(shù)關(guān)系,如果是256則代表 1/256,即為 0.39% 的高端zone內(nèi)存大小。
如果想要預(yù)留更多頁(yè),應(yīng)該設(shè)更小一點(diǎn)的值,最小值是1(1/1 -> 100%)。

  1. 和min_free_kbytes(watermark)的配合示例
    下面是一段某線上服務(wù)器(96G)內(nèi)存申請(qǐng)失敗時(shí)打印出的log:



  2. [38905.295014] java: page allocation failure. order:1, mode:0x20, zone 2
    [38905.295020] Pid: 25174, comm: java Not tainted 2.6.32-220.23.1.tb750.el5.x86_64 #1
    ...
    [38905.295348] active_anon:5730961 inactive_anon:216708 isolated_anon:0
    [38905.295349]  active_file:2251981 inactive_file:15562505 isolated_file:0
    [38905.295350]  unevictable:1256 dirty:790255 writeback:0 unstable:0
    [38905.295351]  free:113095 slab_reclaimable:577285 slab_unreclaimable:31941
    [38905.295352]  mapped:7816 shmem:4 pagetables:13911 bounce:0
    [38905.295355] Node 0 DMA free:15796kB min:4kB low:4kB high:4kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB  isolated(anon):0kB isolated(file):0kB present:15332kB mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:0kB unstable:0kB bounce:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? yes
    [38905.295365] lowmem_reserve[]: 0 1951 96891 96891
    [38905.295369] Node 0 DMA32 free:380032kB min:800kB low:1000kB high:1200kB active_anon:46056kB inactive_anon:10876kB active_file:15968kB inactive_file:129772kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:1998016kB mlocked:0kB dirty:20416kB writeback:0kB mapped:0kB shmem:0kB slab_reclaimable:11716kB slab_unreclaimable:160kB kernel_stack:176kB pagetables:112kB unstable:0kB bounce:0kB writeback_tmp:0kB pages_scanned:576 all_unreclaimable? no
    [38905.295379] lowmem_reserve[]: 0 0 94940 94940
    [38905.295383] Node 0 Normal free:56552kB min:39032kB low:48788kB high:58548kB active_anon:22877788kB inactive_anon:855956kB active_file:8991956kB inactive_file:62120248kB unevictable:5024kB isolated(anon):0kB isolated(file):0kB present:97218560kB mlocked:5024kB dirty:3140604kB writeback:0kB mapped:31264kB shmem:16kB slab_reclaimable:2297424kB slab_unreclaimable:127604kB kernel_stack:12528kB pagetables:55532kB unstable:0kB bounce:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? no
    [38905.295393] lowmem_reserve[]: 0 0 0 0
    [38905.295396] Node 0 DMA: 1*4kB 2*8kB 0*16kB 1*32kB 2*64kB 0*128kB 1*256kB 0*512kB 1*1024kB 1*2048kB 3*4096kB = 15796kB
    [38905.295405] Node 0 DMA32: 130*4kB 65*8kB 75*16kB 72*32kB 95*64kB 22*128kB 10*256kB 7*512kB 4*1024kB 2*2048kB 86*4096kB = 380032kB
    [38905.295414] Node 0 Normal: 12544*4kB 68*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 1*4096kB = 54816kB
    [38905.295423] 17816926 total pagecache pages

1)從第一行l(wèi)og“order:1, mode:0x20”可以看出來(lái)是GFP_ATOMIC類型的申請(qǐng),且order = 1(page = 2 )

2)第一次內(nèi)存申請(qǐng)嘗試

在__alloc_pages_nodemask()里,首先調(diào)用 get_page_from_freelist() 嘗試第一次申請(qǐng),使用的標(biāo)志位是 ALLOC_WMARK_LOW|ALLOC_CPUSET,它會(huì)對(duì)每個(gè)zone都做 zone_watermark_ok()的檢查,使用的就是傳進(jìn)的watermark[low]閾值。
    在zone_watermark_ok()里會(huì)考慮z->lowmem_reserve[],導(dǎo)致在normal上的申請(qǐng)不會(huì)落到低端zone。比如對(duì)于DMA32:
free pages = 380032KB = 95008 pages < low(1000KB = 250 pages) + lowmem_reserve normal = 95190
所以就認(rèn)為DMA32也不平不ok,同理更用不了DMA的內(nèi)存。
而對(duì)于normal自己內(nèi)存來(lái)說,free pages = 56552 KB = 14138 pages,也不用考慮lowmem_reserve(0),但這時(shí)還會(huì)考慮申請(qǐng)order(1),減去order 0的12544個(gè)page后只剩 14138 - 12544 = 1594,也小于 low / 2 = (48788KB=12197pages) / 2 = 6098 pages。
所以初次申請(qǐng)嘗試失敗,進(jìn)入__alloc_pages_slowpath() 嘗試進(jìn)行更為積極一些的申請(qǐng)。

3)第二次內(nèi)存申請(qǐng)嘗試
__alloc_pages_slowpath()首先是通過 gfp_to_alloc_flags() 修改alloc_pages,設(shè)上更為強(qiáng)硬的標(biāo)志位。這塊根據(jù)原來(lái)的GFP_ATOMIC會(huì)設(shè)上 ALLOC_WMARK_MIN | ALLOC_HARDER | ALLOC_HIGH。但注意的是不會(huì)設(shè)上 ALLOC_NO_WATERMARKS 標(biāo)志位。這個(gè)標(biāo)志位不再判斷zone的水位限制,屬于優(yōu)先級(jí)最高的申請(qǐng),可以動(dòng)用所有的reserve內(nèi)存,但條件是(!in_interrupt() && ((p->flags & PF_MEMALLOC) || unlikely(test_thread_flag(TIF_MEMDIE)))),即要求不能在中斷上下文,且是正在進(jìn)行回收(例如kswapd)或者正在退出的進(jìn)程。
    之后進(jìn)入拿著新的alloc_pages重新進(jìn)入get_page_from_pagelist() 嘗試第二次申請(qǐng),雖然有了 ALLOC_HARDER和ALLOC_HIGH,但是不幸的是在3個(gè)zone的zone_watermark_ok檢查中還是都無(wú)法通過,例如對(duì)于DMA32:
free pages = 380032KB = 95008 pages
因?yàn)樵O(shè)上了ALLOC_HIGH 所以會(huì)將得到的watermark[min]減半,即min = min/2 = 800K / 2 = 400K = 100pages
而又因?yàn)樵O(shè)上了ALLOC_HARDER,會(huì)再將min砍去1/4,即min = 3 * min / 4 = 100 pages * 3 / 4 = 75 pages
即便如此,min(75 pages) + lowmem_reserve normal = 95015,仍大于free pages,仍認(rèn)為無(wú)法分配內(nèi)存,同理DMA也不不成功,而normal中 free pages里連續(xù)8K的頁(yè)太少也無(wú)法滿足分配
    第二次失敗后,由于沒有ALLOC_NO_WATERMARK也不會(huì)進(jìn)入__alloc_pages_high_priority 進(jìn)行最高優(yōu)先級(jí)的申請(qǐng),同時(shí)由于是GFP_ATOMIC類型的分配不能阻塞回收或者進(jìn)入OOM,因此就以申請(qǐng)失敗告終。
遇到此種情況可以適當(dāng)調(diào)高 min_free_kbytes 使kswapd較早啟動(dòng)回收,使系統(tǒng)一直留有較多的空閑內(nèi)存,同時(shí)可以適度降低 lowmem_reserve_ratio(可選),使得內(nèi)存不足的情況下(主要是normal zone)可以借用DMA32/DMA的內(nèi)存救急(注意不能也不能過低)。

關(guān)于如何理解kswapd的低水位min_free_kbytes就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI