1. 程式人生 > >Linux內核參數min_free_kbytes

Linux內核參數min_free_kbytes

minimum mode because get comment lac 相加 linux內核參數 space

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.

解釋已經很清楚了,主要有以下幾個關鍵點:
1. 代表系統所保留空閑內存的最低限。
在系統初始化時會根據內存大小計算一個默認值,計算規則是:

  min_free_kbytes = sqrt(lowmem_kbytes * 16) = 4 * sqrt(lowmem_kbytes)(註:lowmem_kbytes即可認為是系統內存大小)

另外,計算出來的值有最小最大限制,最小為128K,最大為64M。
可以看出,min_free_kbytes隨著內存的增大不是線性增長,comments裏提到了原因“because network bandwidth does not increase linearly with machine size”。隨著內存的增大,沒有必要也線性的預留出過多的內存,能保證緊急時刻的使用量便足矣。

2.min_free_kbytes的主要用途是計算影響內存回收的三個參數 watermark[min/low/high]
1) watermark[high] > watermark [low] > watermark[min],各個zone各一套
2)在系統空閑內存低於 watermark[low]時,開始啟動內核線程kswapd進行內存回收(每個zone一個),直到該zone的空閑內存數量達到watermark[high]後停止回收。如果上層申請內存的速度太快,導致空閑內存降至watermark[min]後,內核就會進行direct reclaim(直接回收),即直接在應用程序的進程上下文中進行回收,再用回收上來的空閑頁滿足內存申請,因此實際會阻塞應用程序,帶來一定的響應延遲,而且可能會觸發系統OOM。這是因為watermark[min]以下的內存屬於系統的自留內存,用以滿足特殊使用,所以不會給用戶態的普通申請來用。
3)三個watermark的計算方法:

 watermark[min] = min_free_kbytes換算為page單位即可,假設為min_free_pages。(因為是每個zone各有一套watermark參數,實際計算效果是根據各個zone大小所占內存總大小的比例,而算出來的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。因為min_free_kbytes = 4* sqrt(lowmem_kbytes),也可以看出中間的buffer量也是跟內存的增長速度成開方關系。
4)可以通過/proc/zoneinfo查看每個zone的watermark
例如:

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


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

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會在每個zone上預留一部分內存外,lowmem_reserve_ratio是在各個zone之間進行一定的防衛預留,主要是防止高端zone在沒內存的情況下過度使用低端zone的內存資源。
例如現在常見的一個node的機器有三個zone: DMA,DMA32和NORMAL。DMA和DMA32屬於低端zone,內存也較小,如96G內存的機器兩個zone總和才1G左右,NORMAL就相對屬於高端內存(現在一般沒有HIGH zone),而且數量較大(>90G)。低端內存有一定的特殊作用比如發生DMA時只能分配DMA zone的低端內存,因此需要在 盡量可以使用高端內存時 而 不使用低端內存,同時防止高端內存分配不足的時候搶占稀有的低端內存。

2. 計算方法

cat /proc/sys/vm/lowmem_reserve_ratio
256     256     32


內核利用上述的protection數組計算每個zone的預留page量,計算出來也是數組形式,從/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
       :

在進行內存分配時,這些預留頁數值和watermark相加來一起決定現在是滿足分配請求,還是認為空閑內存量過低需要啟動回收。
例如,如果一個normal區(index = 2)的頁申請來試圖分配DMA區的內存,且現在使用的判斷標準是watermark[low]時,內核計算出 page_free = 1355,而watermark + protection[2] = 3 + 2004 = 2007 > page_free,則認為空閑內存太少而不予以分配。如果分配請求本就來自DMA zone,則 protection[0] = 0會被使用,而滿足分配申請。

zone[i] 的 protection[j] 計算規則如下:

(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)

默認的 lowmem_reserve_ratio[i] 值是:

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

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

3. 和min_free_kbytes(watermark)的配合示例
下面是一段某線上服務器(96G)內存申請失敗時打印出的log:

[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)從第一行log“order:1, mode:0x20”可以看出來是GFP_ATOMIC類型的申請,且order = 1(page = 2 )

2)第一次內存申請嘗試
在__alloc_pages_nodemask()裏,首先調用 get_page_from_freelist() 嘗試第一次申請,使用的標誌位是 ALLOC_WMARK_LOW|ALLOC_CPUSET,它會對每個zone都做 zone_watermark_ok()的檢查,使用的就是傳進的watermark[low]閾值。
在zone_watermark_ok()裏會考慮z->lowmem_reserve[],導致在normal上的申請不會落到低端zone。比如對於DMA32:

free pages = 380032KB = 95008 pages < low(1000KB = 250 pages) +  lowmem_reserve[normal](94940) = 95190

所以就認為DMA32也不平不ok,同理更用不了DMA的內存。
而對於normal自己內存來說,free pages = 56552 KB = 14138 pages,也不用考慮lowmem_reserve(0),但這時還會考慮申請order(1),減去order 0的12544個page後只剩 14138 - 12544 = 1594,也小於 low / 2 = (48788KB=12197pages) / 2 = 6098 pages。
所以初次申請嘗試失敗,進入__alloc_pages_slowpath() 嘗試進行更為積極一些的申請。

3)第二次內存申請嘗試
__alloc_pages_slowpath()首先是通過 gfp_to_alloc_flags() 修改alloc_pages,設上更為強硬的標誌位。這塊根據原來的GFP_ATOMIC會設上 ALLOC_WMARK_MIN | ALLOC_HARDER | ALLOC_HIGH。但註意的是不會設上 ALLOC_NO_WATERMARKS 標誌位。這個標誌位不再判斷zone的水位限制,屬於優先級最高的申請,可以動用所有的reserve內存,但條件是(!in_interrupt() && ((p->flags & PF_MEMALLOC) || unlikely(test_thread_flag(TIF_MEMDIE)))),即要求不能在中斷上下文,且是正在進行回收(例如kswapd)或者正在退出的進程。

之後進入拿著新的alloc_pages重新進入get_page_from_pagelist() 嘗試第二次申請,雖然有了 ALLOC_HARDER和ALLOC_HIGH,但是不幸的是在3個zone的zone_watermark_ok檢查中還是都無法通過,例如對於DMA32:

free pages = 380032KB = 95008 pages
因為設上了ALLOC_HIGH 所以會將得到的watermark[min]減半,即min = min/2 = 800K / 2 = 400K = 100pages
而又因為設上了ALLOC_HARDER,會再將min砍去1/4,即min = 3 * min / 4 = 100 pages * 3 / 4 = 75 pages
即便如此,min(75 pages) +  lowmem_reserve[normal](94940) = 95015,仍大於free pages,仍認為無法分配內存,同理DMA也不不成功,而normal中 free pages裏連續8K的頁太少也無法滿足分配

第二次失敗後,由於沒有ALLOC_NO_WATERMARK也不會進入__alloc_pages_high_priority 進行最高優先級的申請,同時由於是GFP_ATOMIC類型的分配不能阻塞回收或者進入OOM,因此就以申請失敗告終。

遇到此種情況可以適當調高 min_free_kbytes 使kswapd較早啟動回收,使系統一直留有較多的空閑內存,同時可以適度降低 lowmem_reserve_ratio(可選),使得內存不足的情況下(主要是normal zone)可以借用DMA32/DMA的內存救急(註意不能也不能過低)。

Linux內核參數min_free_kbytes