游戏运维 | Swap 与 Swappiness

聂Z腾

2022-11-141086次浏览

0评论

1收藏

2点赞

分享

关于作者

网易游戏私有云平台萌新运维工程师。

一、起

开机!~   各种服务启动!~   各种进程骑上了心爱的系统小摩托,并希望它永远不会堵车。随着系统的运转,越来越多的程序进行使用内存,Linux 使用内存的原则是尽量使用,尽力满足,使得最大限度的使『热』资源存活在内存中,系统就可能面临内存告急的状况,所以内存的管理中有着至关重要的一个环节------回收。

在不关心 LRU 细节,不考虑 NUMA 架构对内存分配的影响的前提下,让我们将目光聚焦在 Swap 的使用。

二、回收的时机

内存的呼神护卫 Kswapd
Kswapd 的任务是试图尽力满足进程对内存的需求,为了满足需求,它必须保证:

  • 当分配页的时候发现满足不了,触发直接回收

  • 空闲低于 watermark[WMARK_MIN] 触发直接回收

  • kswapd 监控

  • 空闲低于 watermark[WMARK_LOW] kswapd 开始回收试图使空闲回归到 watermark[WMARK_HIGH]

  • 空闲达到 watermark[WMARK_HIGH] kswapd 休眠

三、回收的任务

内存的使用分为两个部分:

  • FILE_BASE 文件页

  • ANON_BASE   匿名页

内存回收就是将目前不活跃 (inactive) 的页清理出去,对于对应在磁盘上有真实文件的文件页,如果不是脏页,可以直接释放,如果是脏页就先回写再释放。

但对于没有真实文件对应的匿名页,我们只能使用 Swap 的方式把他们暂时『交换』到交换分区中区。

所以我们也可以看到本质上清理文件页和匿名页本质上都会触发 IO,举个可能不太严谨的栗子:

我们还可以观察得出,文件全部为脏页和换出内存后进程结束都是小概率事件,更重要的是匿名页包括代码段和数据段,『活跃』的可能性远远大于文件页『活跃』的可能性。

所以在系统层面使用 Swap 的代价大于释放文件页。

四、对于 Swap 和 Swappiness 的传统看法

我们可能已有的疑惑

以下采集自 Google 关于 Swap 和 Swappiness 的前两页结果内容,部分错误观点看完应自有结论。

来自 某社区(https://www.v2ex.com/t/149721)的总结

五、内核探秘

我们在 torvalds/linux 的源码(https://github.com/torvalds/linux)中尝试寻找答案

文件 linux/mm/vmscan.c

四种扫描标记:

  • SCAN_FILE (只扫描文件页)

  • 没有 swap 可用

  • 不是 global_reclaim & swappiness 为 0

  • 非活文件页表满足回收页量

  • SCAN_ANON (只扫描匿名页)

    • 是 global_reclaim &   空闲页量 + 文件页量 仍不能满足 高水线 &非活列表够长  &   不活跃的匿名页可以满足本优先级需要回收的页量

  • SCAN_EQUAL(紧急!尽可能扫描所有文件页和匿名页)

    • 优先级为 0 & swappiness 不为 0

  • SCAN_FRACT (根据 swappiness 平衡)

    • 以上都没满足

global_reclaim 与 优先级:

优先级默认为 12,每次回收不足 -1,最紧急状态其值为 0

其中 global_reclaim 为 True 的概率很大!~   请看其定义

继续

到此分析完了完整的内存计算扫描链大小的方式,以及 Swappiness 的使用方式,现在已经基本可以解答之前的怀疑,鉴别错误观点,以及印证来自社区的总结。

我们可以看出内核对于 Swap 的态度已经是避免使用了的。

在文件页的释放能够满足回收需求的时候不关心任何设置不会去使用 Swap,即便在平衡标记下最大限度的使用 Swap 页只是和文件页的比例 1:1,同时在文件页已经不能满足而匿名页可以满足需求的情况下也不会关心任何设置去使用 Swap,另外还可以确定 Swappiness 设置成 0 的风险在于断绝了 SCAN_EQUAL 紧急标记位的可能性。完全没有 Swap 更是只有 SCAN_FILE 一种可能性。

六、思考

Swap 的使用等于给系统开了一扇窗,使得系统在内存回收的过程中多了更多的选择和可能性,在一些情况下可以使用交换匿名页来缓解压力。

关于性能。

传统观念 Swap == Evil ,一些人认为使用到的 Swap 是性能问题,需要想办法让系统不使用 Swap,但其实通过上述分析可以看出系统在内存的使用上已经尽力为我们进行了比较高效率的权衡。

另外,使用 Swap 不是问题,频繁的换入换出才有可能引发性能问题,就如同频繁的释放读取文件页也一样会引发性能问题,所以当问题发生时很大可能是程序需要优化而不是系统需要优化。

通常降低 Swap 的使用能够改善系统状态的情况,只在满足恰好系统对非活匿名页存在误杀,导致刚换出的页又被换入,且降低匿名页的回收同时会增加对文件页的回收而其代价又低于对匿名页的回收时成立。

关于使用原则。

对于 Swap 的空间使用,取决于两点。

一是想要得到的结果:

①在集群下,不希望出现任何抖动/增加延迟/出现慢响应,系统有横向伸缩的能力,可以完全严格不使用 Swap。死掉了再排查原因。避免出现将死不死最为致命的场景,死掉还好说有集群其他节点提供服务,业务基本无感知,而阻塞缺会影响调用链的部分流程挂起,引发其他流程占用资源,甚至集群雪崩。

②是不希望系统发生 OOM,利用任何可利用的资源使系统能够维持运转,能够撑过一些小高峰,可以有选择的利用 Swap。

二是系统的目的:

①要求及时响应的系统趋向尽量避免使用 Swap。

②应用层原意使用内存加速尽量避免使用 Swap,例如:Mysql 内存索引、Redis,触发磁盘 IO 破坏原意。

③系统用来做大量计算,不需要及时响应,可适当利用 Swap 增加吞吐量。Swap 的大小可直接影响系统允许 overcommit 的大小,从而直接决定可以起更多的进程,可以申请更多的匿名页,overcommit 的大小也可以为增加内存提供参考。

关于优化。

加内存固然是解决问题的终极法门,但在不增加成本的前提下,我们也可以有许多种尝试。

①如果有多块磁盘,可以建立多个 Swap 分区挂载到系统,以提高换入换出的效率。

②ZSWAP 的做法是尝试把内核交换出去的页面压缩存放到一个内存池中。ZSWAP 空间也是有限的,ZSWAP 会智能地把其中一些它认为近期不会使用的页面解压缩, 写回到真正的磁盘外设中.   因此, 大部分情况下, 它能避免磁盘写操作。

③使用 Cgroup,为什么 Cgroup 是有效的措施,主要是一个系统的不同进程或进程组目的不尽相同,利用系统全局的设定并不一定合适全部进程,此外在上面代码中可以看到,扫描页的计算对 Cgroup 触发的回收做了多次例外判断能够更倾向于扫描文件页。

④如果你选择关闭 Swap,上述已经可以看到 Swappiness=0 也并不代表关闭 swap,请直接干掉 swap 分区。

七、番外

watermark

  • watermark[WMARK_MIN]:在快速分配失败后的慢速分配中会使用此阀值进行分配,如果慢速分配过程中使用此值还是无法进行分配,那就会执行直接内存回收和快速内存回收

  • watermark[WMARK_LOW]:也叫低阀值,是快速分配的默认阀值,在分配内存过程中,如果 zone 的空闲页框数量低于此阀值,系统会对 zone 执行快速内存回收

  • watermark[WMARK_HIGH]:也叫高阀值,是 zone 对于空闲页框数量比较满意的一个值,当 zone 的空闲页框数量高于这个值时,表示 zone 的空闲页框较多。所以对 zone 进行内存回收时,目标也是希望将 zone 的空闲页框数量提高到此值以上,系统会使用此阀值用于 oomkill 进行内存回收。

      这三个阀值的关系是:min 阀值 < low 阀值 < high 阀值。在系统初始化期间,根据系统中整个内存的数量与每个 zone 管理的页框数量,计算出每个 zone 的 min 阀值,然后 low 阀值 = min 阀值 + (min 阀值 / 4),high 阀值 = min 阀值 + (min 阀值 / 2)。这样就得出了这三个阀值的数值,我们可以通过 /proc/zoneinfo 中查看这三个阀值的数值。

其中 MIN 的值是依据 min_free_kbytes 计算得出,所以 min_free_kbytes 的值直接影响系统的稳定性, min_free_kbytes 设的越大,watermark 的线越高,同时三个线之间的 buffer 量也相应会增加。

这意味着会较早的启动 kswapd 进行回收,且会回收上来较多的内存(直至 watermark[high] 才会停止),这会使得系统预留过多的空闲内存,从而在一定程度上降低了应用程序可使用的内存量。

极端情况下设置 min_free_kbytes 接近内存大小时,留给应用程序的内存就会太少而可能会频繁地导致 OOM 的发生。min_free_kbytes 设的过小,则会导致系统预留内存过小。

kswapd 回收的过程中也会有少量的内存分配行为(会设上 PF_MEMALLOC )标志,这个标志会允许 kswapd 使用预留内存;另外一种情况是被 OOM 选中杀死的进程在退出过程中,如果需要申请内存也可以使用预留部分。

这两种情况下让他们使用预留内存可以避免系统进入 deadlock 状态。

鉴于加四分之一计算 LOW 再加四分之一计算 HIGH 这种方式过于死板现在又引入了 watermark_scale_factor

八、CommitLimit

Memory Overcommit 的意思是操作系统承诺给进程的内存大小超过了实际可用的内存。

CommitLimit 就是 overcommit 的阈值,申请的内存总数超过 CommitLimit 的话就算是 overcommit。

在 vm.overcommit_memory   为默认值『试探式的』进行 overcommit 的分配方式下(http://linuxperf.com/?p=102),可以看出 Swap 的大小直接决定了可以超分的量。利用这个特性可以灵活增加系统吞吐量。

九、Ref

tolimit Blog 内存源码分析
https://www.cnblogs.com/tolimit/tag/%E5%86%85%E5%AD%98/

Arnold Lu 内存管理
https://www.cnblogs.com/arnoldlu/category/1132616.html

现在的 Linux 内核和 Linux 2.6 的内核有多大区别?
https://www.zhihu.com/question/35484429

文中提到的V站关于Swap的讨论
https://www.v2ex.com/t/149721

评论 0

0/1000
网易游学APP
为热爱赋能
扫描二维码下载APP