Linux之OOM系列

0    110    3

👉 本文共约6318个字,系统预计阅读时间或需24分钟。

防止OOM导致服务器卡死

Linux 系统下应避免因内存耗尽导致系统 Hang 住或频繁读写 Swap 的情况!

内存不足(OOM) 是计算机操作经常不希望出现的状态,在这种状态下,无法分配其他内存供程序或操作系统使用。这样的系统将无法加载任何其他程序,并且由于许多程序可能在执行期间将其他数据加载到内存中,因此这些程序将无法正常运行。这通常是因为已分配了所有可用内存,包括磁盘交换空间。

  • 内存溢出

申请的内存超出了程序能提供的内存大小,此时称之为溢出。

  • 内存泄露

程序申请使用完的内存没有得到及时释放,导致其他程序不能再次使用该内存,此时这段内存就泄露了。因为申请者不用了,而又不能被其他程序使用。

OOM导致服务器卡死

OOM导致服务器卡死


1. 了解 OOM 机制

Linux 系统下应避免因内存耗尽导致系统 Hang 住或频繁读写 Swap 的情况!

如果运维过服务器的话,肯定或多或少会遇到系统 OOM 的情况,这种情况多出现在系统内存严重不足的时候。在系统 OOM 之前,我们会发现系统巨卡,如果上面部署服务的话,肯定会严重影响使用的。

  • 理解 OOM 的作用

Linux 系统中,out of memory 指的是由于系统内存压力,系统会选择保护一些系统进程,而将一些其他的进程 kill 掉,来释放内存,缓解系统内存压力。

  • 理解 swap 交换的作用

Linux 系统中,当物理内存不够用的时候,而又有新的程序需要分配内存,此时 Linux 内核就会选择将其他程序(已运行)暂时不用的数据交换到物理磁盘上(swap out),等该程序需要用的时候再读进来(swap in)。而交换是有代价的,而磁盘 IO 代价大家都懂的。


2. 简单处理方式

Linux 系统下应避免因内存耗尽导致系统 Hang 住或频繁读写 Swap 的情况!

这是因为,在系统 OOM 之前会做一些“清理”工作,比如 Linux 会尝试清理内存页缓存会清空所有 buffer,但是这种危急情况下系统会进入呆滞状态,响应非常缓慢。

OOM导致服务器卡死 - Boom

OOM导致服务器卡死 - Boom

这个时候系统就非常卡了,很容易遇到系统 Hang 住的情况,到最后不得不重启机器。而我们日常的清理内存操作,因为系统内存足够,所以在我们手动执行如下命令之后,开始时会比较卡一会儿,而并不会出现死机的情况。

因为这个问题,我们苦苦挣扎,辗转难以入睡。希望操作系统一直保持正常运行,即便提前干掉耗内存大的进程。但是,使用内核提供的 OOM Killer 无法实现这一目标。

如果“临时”使用的话,可以在我们启动服务之前,通过 choom 命令来进行优先级的设置。这样当系统内存不足时,会优先/延后杀掉我们的服务。


3. 优雅处理方式

Linux 系统下应避免因内存耗尽导致系统 Hang 住或频繁读写 Swap 的情况!

而在用户空间中,我们可以做任何我们想做的事情,包括“自杀”。所以,我们监控在系统内存不足的情况下,决定杀掉它它它,来释放内存。当然这里我们不用自己去实现上述逻辑来删除系统进程,只要使用 EarlyOOM 这个工具就可以完成我们的心愿。

当系统内存不足时,earlyOOM 会在系统可用内存以及 swap 都必须低于 10% (默认值-PERCENT)的话,这时 earlyoom 才会给占用内存最多的进程发 SIGTERM 信号,如果可用内存继续下降到 5% (默认值-PERCENT/2)的话,将发送 SIGKILL 信号终止内存最大的进程(即 oom_score 最大的)从而释放内存。

  • earlyoom
    • Early OOM Daemon for Linux
    • config: /etc/default/earlyoom
  • nohang
    • A sophisticated low memory handler for Linux

其实最好的解决方法,还是从代码层面进行修改和调整。根据以往的经验来说,大部分情况下都是因为代码的逻辑或者使用方式不对,导致某个/多个操作需要占用大量内存且短时间不会释放。与此同时,程序可以限制下内存的使用,尽量避免导致系统 OOM 出现。最后是,通过装像 earlyoom 这类 pre-oom 的工具来保障系统和服务的正常使用。


4. 工具使用方式

Linux 系统下应避免因内存耗尽导致系统 Hang 住或频繁读写 Swap 的情况!

通过上述命令安装完成之后,就可以根据我们系统的实际情况来修改配置文件了。earlyoom 的配置比较简单,基本是零配置。可能唯一需要注意的是,如果内存太大,建议通过 -M 来指定触发的时机点。通过执行 man earlyoom 命令,可以看到完整的帮助信息。

  • 增加 -m 10 参数
  • 增加 -M 1048576 参数
    • 将触发 terminate 的时机限定为 min(10%, 1G)
    • 这样避免在内存充足(还有xG内存)的情况下,进程被强制退出
  • 增加 -S xxx 参数
    • 设置 swap 的最小交换分区大小(单位为KiB)
  • 增加 -r xxx 参数
    • 设置内存报告间隔(默认为1秒),设置为 0 表示完全禁用
  • 增加 --avoid xxx 参数
    • 指定避免杀掉的进程 - 正则表达式
    • 实例说明:earlyoom --avoid '^(foo|bar)$'
  • 增加 --prefer xxx 参数
    • 设置优先杀掉的进程 - 正则表达式
    • 实例说明:earlyoom --prefer '^(foo|bar)$'
  • 增加 -p 参数
    • 将 Earlyoom 的优先级设置为-20,将 oom_score_adj 设置为-100
  • 增加 --dryrun 参数
    • 设置为干跑模式(不会删除任何进程)

  • 如果是通过源代码编译安装,则直接启动就可以使用了。
  • 输出的信息,包括使用的内存和 swap 状态。

  • 如果是通过 systemd 服务安装的话,则会自动后台运行。
  • 输出的信息,包括使用的内存和 swap 状态。


5. 完整参数选项

Linux 系统下应避免因内存耗尽导致系统 Hang 住或频繁读写 Swap 的情况!

  • 命令的完整参数选项,如下所示:

防止程序OOM时退出

Linux 系统上面总有一个小众但神奇的命令!

防止程序OOM时退出

防止程序OOM时退出


当物理内存和交换空间不够用时,OOM Killer 就会选择杀死进程,那么它是怎样知道要先杀死哪个进程呢?其实 Linux 的每个进程都有一个 oom_score (位于/proc/<pid>/oom_score文件中),这个值越大,就越有可能被 OOM Killer 选中。

本人提供Oracle、MySQL、PG等数据库的培训和考证业务,私聊QQ646634621或微信db_bao,谢谢!
  • 如果进程消耗的内存越大,它的 oom_score 通常也会越大。
  • 如果进程运行了很长时间,并且消耗很多 CPU 时间,那么通常它的 oom_score 会偏小。
  • 如果进程以 Superuser 的身份运行,那么它的 oom_score 也会偏小。

如何才能尽量防止某个重要的进程被杀死呢?Linux 每个进程都有一个 oom_adj(位于/proc/<pid>/oom_adj文件中),这个值的范围是 [-17, +15],默认值为 0,值越大越容易被 kill 掉。如果设置为 -17 的话,表示永远不会被清除。进程的 oom_adj 会影响 oom_score 的计算,也就是说,我们可以通过调小进程的 oom_adj 从而降低进程的 oom_score


1. 问题现象

小问题引发的,小小思考!

事情是这样的,我们一个服务因为跑任务导致服务器的内存使用过多,导致系统出现 OOM 的情况。然后启动了系统保护,把 supervisor 服务给杀了,但是他启动的子进程没有杀完。导致任务跑完之后,内存释放了,而 ci 对应的之前子进程没有杀完且启动了新的进程,导致服务奇怪的异常出现。

supervisor 官方 issus 中发现了下面这个问题,很早之前就有人提出了这个问题,并且给了自己的补丁方法,但是项目的维护者不知道出于什么考虑,导致这个问题已经没有合入也没有任何解释。我在下面 @ 了两个核心开发者,但是也是没有回复。但是,你却发现他们还是在不断地提交代码和合入代码,我有点方了。

现在就是继续关注这个问题,看看之后什么会合入或者修复,目前看还是只有考虑使用其他的方式来规避这个问题的出现。

防止程序OOM时退出

防止程序OOM时退出


2. 问题原因

知其然,知其所以然!

Linux 系统中的 OOM killer 是为了在内存不足的情况下,杀掉消耗内存最多、优先级最低的任务,从而保障系统可以最基本的正常运行。内核通过计算内存消耗,得出 oom_score 的数值。同时 oom_score_adj 允许用于自定义,我们理解为程序运行的优先级,优先级越高对应的值越小,即越小越不容易被杀,范围在 -1000~1000 之内。

在系统内存紧张的时候,内核通过 oom = oom_score + oom_score_adj 公式,计算出分数最高的进程,向其发送关闭信号。


3. 计算示例

加强对于上面说的公式的计算!

说了这么多,那么我们一起找个进程,具体看看各部分到底是怎么计算出来的。


4. 代码逻辑

我自己没有看过源代码,只是转贴了代码片段!

我们上面只是对其 OOM(Out of memory) 得分怎么计算给出了直接公式,已经其对应子项的计算方法。通过公式,我们可以很快的计算出进程的得分情况,来判断哪些进程会被优先终止掉。而下面则是,程序中对应的代码实现细节。

  • 计算 oom 的代码

  • 找出 oom 最大的进程

  • 发送 kill 信号关闭进程


5. 解决办法

我们现在已经知道了原理,那怎么处理这个问题呢?

  • 1.增大系统内存

问题就是因为内存不够导致的,简单粗暴也最有效,我们增加存在就行了!

  • 2.升级到 64 位操作系统

因为在 64 位的操作系统没有对 low-memory 限制。

  • 3.使用 hugemem 内核

这种内核以不同的方式分割 low/high memory,而且在大多数情况下会提供足够多的 low memoryhigh memory 的映射。在大多数案例中,这是一个很简单的修复方法:安装 hugemem kernel RPM 包,然后重启即可。

  • 4.配置 OOM killer

通过一些内核参数来调整 OOM killer 的行为,避免系统在那里不停的杀进程。比如我们可以在触发 OOM 后立刻触发 kernel panickernel panic 10 秒后自动重启系统。

  • 5.关闭/打开oom-killer(慎用)

  • 6.通过choom命令设置优先级


6. 参考链接

送人玫瑰,手有余香!

参考

https://www.escapelife.site/posts/c74f29f9.html

https://www.escapelife.site/posts/8551b7b4.html

标签:

头像

小麦苗

学习或考证,均可联系麦老师,请加微信db_bao或QQ646634621

您可能还喜欢...

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

20 − 17 =

 

嘿,我是小麦,需要帮助随时找我哦
  • 18509239930
  • 个人微信

  • 麦老师QQ聊天
  • 个人邮箱
  • 点击加入QQ群
  • 个人微店

  • 回到顶部
返回顶部