最新消息:XAMPP默认安装之后是很不安全的,我们只需要点击左方菜单的 "安全"选项,按照向导操作即可完成安全设置。

Linux内存调节之zone watermark

XAMPP案例 中文小张 1618浏览 0评论

前面的文章提到“什么情况下触发direct reclaim,什么情况下又会触发kswapd,是由内存的watermark决定的”,那这个”watermark”到底是如何发挥作用的呢?

Kswapd与Watermark

Linux中物理内存的每个zone都有自己独立的min, low和high三个档位的watermark值。在进行内存分配的时候,如果分配器(比如buddy allocator)发现当前空余内存的值低于”low”但高于”min”,说明现在内存面临一定的压力,那么在此次内存分配完成后,kswapd将被唤醒,以执行内存回收操作。在这种情况下,内存分配虽然会触发内存回收,但不存在被内存回收所阻塞的问题,两者的执行关系是异步的。

这里所说的空余内存其实是一个zone总的空余内存减去其lowmem_reserve的值,关于lowmem_reserve将在下篇文章中介绍。为了叙述的简便,以下都直接使用”空余内存”一词。

对于kswapd来说,要回收多少内存才算完成任务呢?只要把空余内存的大小恢复到”high”对应的watermark值就可以了,当然,这取决于当前空余内存和”high”值之间的差距,差距越大,需要回收的内存也就越多。”low”可以被认为是一个警戒水位线,而”high”则是一个安全的水位线。

如果内存分配器发现空余内存的值低于了”min”,说明现在内存严重不足。这里要分两种情况来讨论,一种是默认的操作,此时分配器将同步等待内存回收完成,再进行内存分配,也就是direct reclaim。还有一种特殊情况,如果内存分配的请求是带了PF_MEMALLOC标志位的,并且现在空余内存的大小可以满足本次内存分配的需求,那么也将是先分配,再回收。

使用PF_MEMALLOC(“PF”表示per-process flag)相当于是忽略了watermark,因此它对应的内存分配的标志是ALLOC_NO_WATERMARK。能够获取”min”值以下的内存,也意味着该process有动用几乎所有内存的权利,因此它也对应GFP的标志__GFP_MEMALLOC。

if (gfp_mask & __GFP_MEMALLOC)
return ALLOC_NO_WATERMARKS;

if (alloc_flags & ALLOC_NO_WATERMARKS)
set_page_pfmemalloc(page);

那谁有这样的权利,可以在内存严重短缺的时候,不等待回收而强行分配内存呢?其中的一个人物就是kswapd啦,因为kswapd本身就是负责回收内存的,它只需要占用一小部分内存支撑其正常运行(就像启动资金一样),就可以去回收更多的内存(赚更多的钱回来)。

虽然kswapd是在”low”到”min”的这段区间被唤醒加入调度队列的,但当它真正执行的时候,空余内存的值可能已经掉到”low”以下了。可见,”min”值存在的一个意义是保证像kswapd这样的特殊任务能够在需要的时候立刻获得所需内存。

Watermark的取值

那么这三个watermark值的大小又是如何确定的呢?ZONE_HIGHMEM的watermark值比较特殊,但因为现在64位系统已经不再使用ZONE_HIGHMEM了,为了简化讨论,以下将以不含ZONE_HIGHMEM,且只有一个node的64位系统为例进行讲解。

在这种系统中,总的”min”值约等于中所有zones可用内存的总和乘以16再开平方的大小,可通过”/proc/sys/vm/min_free_kbytes”查看和修改。假设可用内存的大小是4GiB,那么其对应的”min”值就是8MiB。

int __meminit init_per_zone_wmark_min(void)
{
min_free_kbytes = int_sqrt(lowmem_kbytes * 16);

if (min_free_kbytes < 128)
min_free_kbytes = 128;
if (min_free_kbytes > 65536)
min_free_kbytes = 65536;

}

这里的”min”值有个下限和上限,就是最小不能低于128KiB,最大不能超过65536KiB。在实际应用中,通常建议为不低于1024KiB。

得到总的”min”值后,我们就可以根据各个zone在总内存中的占比,通过do_div()计算出它们各自的”min”值。假设总的”min”值是8MiB,有ZONE_DMA和ZONE_NORMAL两个zones,大小分别是128MiB和896MiB,那么ZONE_DMA和ZONE_NORMAL的”min”值就分别是1MiB和7MiB。

void __setup_per_zone_wmarks(void)
{
unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT – 10);

for_each_zone(zone) {
tmp = (u64)pages_min * zone->managed_pages;
do_div(tmp, lowmem_pages);

zone->watermark[WMARK_MIN] = tmp;
zone->watermark[WMARK_LOW]  = min_wmark_pages(zone) + (tmp >> 2);
zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + (tmp >> 1);

}

一个zone的”low”和”high”的值都是根据它的”min”值算出来的,”low”比”min”的值大1/4左右,”high”比”min”的值大1/2左右,三者的比例关系大致是4:5:6。

使用”cat /proc/zoneinfo”可以查看这三个值的大小(注意这里是以page为单位的):

你可以把”/proc/zoneinfo”中所有zones的”min”值加起来乘以4(如果page size是4KiB的话),看一下是不是基本等于”/proc/sys/vm”中的”min_free_kbytes”的值。

Watermark的调节

为了尽量避免出现direct reclaim,我们需要空余内存的大小一直保持在”min”值之上。在网络收发的时候,数据量可能突然增大,需要临时申请大量的内存,这种场景被称为”burst allocation”。此时kswapd回收内存的速度可能赶不上内存分配的速度,造成direct reclaim被触发,影响系统性能。

在内存分配时,只有”low”与”min”之间之间的这段区域才是kswapd的活动空间,低于了”min”会触发direct reclaim,高于了”low”又不会唤醒kswapd,而Linux中默认的”low”与”min”之间的差值确实显得小了点。

为此,Android的设计者在Linux的内存watermark的基础上,增加了一个”extra_free_kbytes”的变量,这个”extra”是额外加在”low”与”min”之间的,它在保持”min”值不变的情况下,让”low”值有所增大。假设你的”burst allocation”需要100MiB(100*1024KiB)的空间,那么你就可以把”extra_free_kbytes”的值设为102400。

转载请注明:XAMPP中文组官网 » Linux内存调节之zone watermark

您必须 登录 才能发表评论!