当前位置: 首页 > Linux

LinuxKernelVSMemoryFragmentation(Part2)

时间:2023-04-06 19:41:09 Linux

从LinuxKernelVSMemoryFragmentation(Part1)可以看出,按照迁移类型分组只是延缓了内存碎片,并没有从根本上解决,所以随着时间的推移,当内存碎片过多,不能满足连续的物理内存需求时,就会造成性能问题。因此,仅仅依靠这个功能是不够的,所以内核引入了内存调节等功能。内存调节在内存调节介绍之前,内核使用lumpyreclaim来防碎片,但是在我们最常用的3.10版本内核上已经不存在了,就不介绍了。有兴趣的小伙伴,请从文章开头开始整理,先从列表开始,再来看看内存调节。内存压缩算法思想在内存压缩中有详细描述。简单的说就是从selectedzone底部开始扫描分配的迁移类型MIGRATE_MOVABLEpages,然后从zone顶部开始扫描freepages,将底部的movablepages移动到顶部。空闲页面,在底部形成一个连续的空闲页面。Selectedfragmentationzone:Scanningmovablepages:Scanningfreepages:Finishing:看似原理比较简单,内核也提供了手动压缩的接口:/proc/sys/vm/compact_memory,其实就是如中所说前言中说了(至少对于我们最常用的3.10版本内核而言)无论是手动触发还是自动触发,内存正则化都不好用,开销太大,反而成为性能瓶颈:Memorycompactionissues.不过社区并没有放弃这个功能,而是选择继续完善。比如mm,compaction:v4.6版本引入了kcompactd,v4.8版本引入了makedirectcompactionmoredeterministic。对于3.10内核,内存整合的时机如下:分配高层内存失败后,kswapd线程平衡zone;直接内存回收满足高层内存需求,包括THP缺页异常处理路径;khugepaged内核线程试图折叠一个巨大的页面;通过/proc接口手动触发内存调节;其中,与THP相关的路径,我在上一篇为什么要禁用THP的文章中提到了它的危害,建议大家关闭,所以这里就不分析THP路径了,主要关注内存分配路径:基本过程:When申请分配页面,如果无法从伙伴系统的freelist中获取到页面,则会进入慢速内存分配路径,首先使用低水位线尝试分配。如果失败,说明内存略有不足。页面分配器唤醒kswapd线程以在尝试使用最低水位线分配页面之前异步回收页面。如果分配失败,说明剩余内存严重不足,会先进行异步内存正则化。如果异步正则化后无法分配页面,则进行直接内存回收,或者回收的页数仍不能满足需求,则进行直接内存正则化。直接内存回收如果没有收到页面,调用oomkiller回收内存。上述过程只是一个简化的描述。实际的工作流程会复杂得多。根据不同应用程序的顺序和flags的分配,上述过程会略有变化。为避免纠缠细节,本文不做展开。为了方便量化分析每个参与线程直接内存回收和内存正则化带来的延迟,我在BCC项目中提交了两个工具:drsnoop和compactsnoop。这两个工具的文档都很详细,但是分析的时候要注意一点。为了减少BPF引入的开销,这里捕获的每个对应事件的延迟与申请内存的事件可能是多对一的关系。对于3.10等老内核,在缓慢的内存分配过程中重试次数不确定,导致oomkiller出现的时间过早或过晚,导致服务器上大部分任务长期处于挂起状态.内核在4.12版本合并到mm:fix100%CPUkswapdbusylooponunreclaimablenodes,限制直接内存回收的最大次数。再来看最大次数16,假设直接内存回收的平均延迟为10ms(对于100G内存的服务器,shrinkactive/inactivelru链表是非常耗时的,如果需要等待dirtypages回写会有额外的延迟)。因此,当一个线程通过页面分配器申请一个页面时,只需要执行一次直接内存回收就可以回收到足够的内存,那么这次分配内存的增加延迟增加了10ms,需要重试16次才能回收到足够的内存。页面,增加的延迟不再是10ms,而是160ms。让我们回顾一下内存调节。内存调节的核心逻辑主要分为4个步骤:判断内存区域是否适合内存调节;设置扫描的起始页帧数;隔离MIGRATE_MOVABLE属性页;将MIGRATE_MOVABLE属性页迁移到区域顶部;执行一次迁移后,如果还需要继续正则化,则循环执行34,直到正则化完成。因此,它会消耗大量的CPU资源。从监控上经常看到syscpu满了。页面迁移也是一个大话题。除了内存调节,内存迁移还有其他场景用到,这里就不展开了。下面看看如何判断zone是否适合进行内存调控:对于通过/proc接口强制调控的情况,没什么好说的,服从命令,听指挥;使用应用程序的顺序来判断区域是否有足够的剩余内存进行调节(不同版本的内核算法细节存在差异)。计算碎片指数。当索引接近0时,意味着内存分配失败,内存不足。所以这个时候不适合做内存调节,而适合做内存回收。当索引接近1000时,意味着内存分配会因为外部碎片太多而失败,所以不适合内存回收,适合内存调节。这里,调节和回收的分界线是由外部碎片阈值决定的:/proc/sys/vm/extfrag_threshold;内核开发者也给我们提供了观察内存索引的接口:可以通过执行cat/sys/kernel/debug/extfrag/extfrag_index来观察(这里有一个小数点,因为1000除外)。结束语本文简要介绍了为什么外部内存碎片会导致性能问题,以及社区多年来在反碎片方面所做的努力。重点介绍了3.10版本内核反碎片化的原理和定量定性观察方法。期待对大家有所帮助。之所以在描述内存正则化时顺便提到直接内存回收,是因为直接内存回收不仅会在内存严重不足的情况下发生,而且在实际场景中内存碎片也会触发直接内存回收。可能会混合出现。本文还介绍了基于/proc文件系统的监控接口和基于内核事件的工具。工具可以解决这些问题,但是需要对内核相关子系统的工作原理有一定的了解,并且对客户的内核版本有一定的要求。至于如何降低直接内存回收的频率以及如何缓解碎片问题,我的想法是对于需要大量IO操作的工作负载场景,由于内核设计是为了照顾慢速后端设备,对于例如,它是在lru算法的基础上实现的secondchancemethod,RefaultDistance等,并没有提供限制pagecache比例的能力(有些公司已经为自己的内核定制了这个功能,并试图提交给上游内核社区,但是上游社区还没有接受,个人认为可能存在workingsetdefault等问题)。因此,对于100G以上大内存机器的场景,增加vm.min_free_kbytes来变相限制pagecache的比例(最大不要超过总内存的5%)是更好的选择。虽然增加vm.min_free_kbytes确实会造成一些内存浪费,但是对于256G内存的服务器,我们设置为4G,只占1.5%。社区显然也注意到了这一点,并将mm:scalekswapdwatermarks按内存比例合并到4.6版本的内核中以对此进行优化。另一种方法是在适当的时候进行dropcache,但是可能会给业务带来较大的抖动。期待您的交流和反馈!