OOM看内存之低端内存保护机制lowmem 您所在的位置:网站首页 go内存分配器 OOM看内存之低端内存保护机制lowmem

OOM看内存之低端内存保护机制lowmem

2023-06-27 20:38| 来源: 网络整理| 查看: 265

一 什么是lowmem_reserve

为了防止高端内存申请者”偷用”太多的低端内存,内核的内存页分配器提供了一种叫做”lowmem_reserve”的机制防止来防止高端内存的申请者占用太多低端内存,这个机制是通过”lowmem_reserve_ratio”这个调节接口来决定低端内存被高端内存占用的程度。

lowmem_reserve_ratio在内核中是一个整形数组,可通过如下proc接口文件访问(linux-5.10版本):

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

这个数组的默认值在内核定义如下:

int sysctl_lowmem_reserve_ratio[MAX_NR_ZONES] = { #ifdef CONFIG_ZONE_DMA [ZONE_DMA] = 256, #endif #ifdef CONFIG_ZONE_DMA32 [ZONE_DMA32] = 256, #endif [ZONE_NORMAL] = 32, #ifdef CONFIG_HIGHMEM [ZONE_HIGHMEM] = 0, #endif [ZONE_MOVABLE] = 0, };

需要注意的是,lowmem_reserve_ratio只是一个系数,并不是直接可用的数值,内核函数setup_per_zone_lowmem_reserve()通过lowmem_reserve_ratio这个系数来计算出各个zone的保留内存。各个zone的保留内存值在/proc/zoneinfo获取到,如下所示:

Node 0, zone DMA …… pages free 2655 min 70 low 87 high 104 spanned 4095 present 3998 managed 3840 cma 0 protection: (0, 2740, 3567, 3567, 3567)

这里的protections可作为一个判断这个zone是否有足够分配内存的一个因素。

在上例中,如果一个normal内存页(index=2)要申请DMA的内存且使用到watermark[WMARK_HIGH]水线,内核会做如下判断:

free_pages = 2655 watermark(WMARK_HIGH)+protection[2] = 104+3567 = 3671

因此free_pages < watermark(WMARK_HIGH)+protection[2] ,so,这次分配会失败。这里使用protection[2]是因为normal内存的index是2;如果请求的是DMA内存,则index=0,则使用protection[0]。

二 如何计算各个zone的lowmem_reserve

其实就是关于如何计算各个zone的protection[]。

系统在初始化时会调用根据setup_per_zone_lowmem_reserve计算各个zone的lowmem_reserve;此外,还可以在系统启动后通过/proc/sys/vm/lowmem_reserve_ratio接口调整各个zone的lowmem_reserve,具体计算公式参考如下伪代码:

(i protection[j] = (total sums of managed_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)

有了伪代码,我们就更好理解下面的内核的计算代码,如下所示:

/* * setup_per_zone_lowmem_reserve - called whenever * sysctl_lowmem_reserve_ratio changes. Ensures that each zone * has a correct pages reserved value, so an adequate number of * pages are left in the zone after a successful __alloc_pages(). */ static void setup_per_zone_lowmem_reserve(void) { struct pglist_data *pgdat; enum zone_type i, j; for_each_online_pgdat(pgdat) { for (i = 0; i < MAX_NR_ZONES - 1; i++) { struct zone *zone = &pgdat->node_zones[i]; int ratio = sysctl_lowmem_reserve_ratio[i]; bool clear = !ratio || !zone_managed_pages(zone); unsigned long managed_pages = 0; /** i=0: zone[DMA].lowmem_reserve[DMA32] = managed_pages(zone[DMA32]) / ratio[DMA] zone[DMA].lowmem_reserve[NORMAL] = (managed_pages(zone[NORMAL]) + managed_pages(zone[DMA32])) / ratio[DMA] i=1: zone[DMA32].lowmem_reserve[NORMAL] = managed_pages(zone[NORMAL]) / ratio[DMA32] **/ for (j = i + 1; j < MAX_NR_ZONES; j++) { struct zone *upper_zone = &pgdat->node_zones[j]; managed_pages += zone_managed_pages(upper_zone); if (clear) zone->lowmem_reserve[j] = 0; else zone->lowmem_reserve[j] = managed_pages / ratio; } } } /* update totalreserve_pages */ calculate_totalreserve_pages(); }

 

三 lowmem_reserve对于内存页分配的影响

内核在分配内存页时会调用__zone_watermark_ok()函数去检查zone中是否有足够的内存,其中的一个判定条件就是:

if (free_pages lowmem_reserve[highest_zoneidx])

其中,free_pages就是对应zone中的剩余可用内存;而min就是经过计算后的水线;z->lowmem_reserve[highest_zoneidx]就是该zone对于highest_zoneidx的预留内存。如果上述条件不满足,本次扫描会跳过这个zone,表示这个zone内存不足。

从这个代码逻辑来看,lowmem_reserve对于内存页的分配确实起着至关重要的作用,决定着内存分配成败。

在实际实践过程中如果发生OOM时看到free内存并未触及到水线时,可以看看是否是由于z->lowmem_reserve影响~ 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有