另外,计算出来的值有最小最大限制,最小为128K,最大为64M。
可以看出,min_free_kbytes随着内存的增大不是线性增长,comments里提到了原因“because network bandwidth does not increase linearly with machine size”。随着内存的增大,没有必要也线性的预留出过多的内存,能保证紧急时刻的使用量便足矣。
2.min_free_kbytes的主要用途是计算影响内存回收的三个参数 watermark[min/low/high]
- 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的低端内存,因此需要在 尽量可以使用高端内存时 而 不使用低端内存,同时防止高端内存分配不足的时候抢占稀有的低端内存。
- 计算方法
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%)。
- 和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:
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
详情docs.qq.com/doc/DSmdCdUNwcEJDTXFK
LV2ruv-1724670073638)]
[外链图片转存中…(img-BPeDhIeT-1724670073638)]
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
详情docs.qq.com/doc/DSmdCdUNwcEJDTXFK