oom 机制

说明

  • 使用linux系统,如果物理内存不足内核会打印Out of memory信息,并伴随着某些用户态进程被kill掉,在嵌入式开发中比较容易出现,如下:
Out of memory: Kill process 9682(mysqld) score 9 or sacrifice child
Killed process 9682, UID 27,(mysqld) total-vm:47388kB, anon-rss:3744kB, file-rss:80kB
  • oom是"Out of memory"的缩写,意思是超出内存资源范围,物理内存不足。

oom与内存申请

  • 物理内存不足为什么不是返回内存申请失败,反而让oom kill机制kill掉进程?因为申请内存首先是申请的虚拟内存,使用时再映射到物理内存,并不是直接操作物理内存,具体原因如下:
  1. 很多时候应用程序申请内存,并不会立刻使用内存,因此并不会立刻申请物理内存,例如:
* 在PC平台上,编写两个测试程序,通过free -m观察内存
1. 使用malloc申请10M内存。
2. 使用calloc申请10M内存。
* 测试发现运行程序1,内存不会发生变化,运行程序2,内存会减少10M。
* 原因在于:malloc申请内存,并不会对内存做操作,所以没必要保存到物理内存上,而calloc申请内存后需要将内存空间清空,所以需要立刻申请物理内存
* 有些嵌入式平台,使用calloc,内存也不会减少,说明该平台calloc底层实现和malloc一样,未分配物理内存。
  1. 不确定所需内存大小时,很多应用层程序会申请大块内存以防万一,但是有时可能只使用到其中的一小块,因此并不会完全按照申请的内存分配物理内存,例如:
* 在PC平台上,编写一个测试程序,使用malloc申请10M内存,但是只对其中一个字节进行赋值,通过free 观察内存。
* 测试发现运行程序后内存并没有减少10M,只减少了24KB。
  1. 内存数据并不是时刻在使用的,开启swap机制后,Linux系统可以将使用频率较低的内存页转移至swap空间,以释放物理内存,因此内存申请可以超过物理内存大小。
  • 什么情况下申请内存会分配物理内存,什么时候不会?
  1. 虚拟内存映射到物理内存是由系统进行控制的,对程序员是透明的,不是非常必要搞明白是如何控制的,但是我们应该知道:对内存地址进行赋值,进行操作时,该地址一定在物理内存上,即使不在,也会被置换到内存页上。
  • 既然可以申请超过物理内存大小的内存空间,为什么有时程序中内存申请会失败?
  1. 对于不会立刻申请物理内存的申请操作,是否申请成功由overcommit配置决定,是通过一些条件判断决定的,并不是真正的物理内存申请失败。
  2. 对于会立即申请物理内存的申请操作,是否成功先判断overcommit配置再真实分配。

overcommit

  • overcommit意思是超额承诺。
  • linux系统用来设置:应用层程序内存申请超出物理内存大小范围时的处理,并提供了两个接口来对其设置。
/proc/sys/vm/overcommit_memory
/proc/sys/vm/overcommit_ratio
  • overcommit_memory默认值为0,并且支持三个选项,如下:
  1. 值为0:表示程序申请内存时,系统会判断当前剩余物理内存,以决定申请是否成功。
  2. 值为1:表示程序申请内存每次都返回成功,直至物理内存不足。
  3. 值为2:表示不容许任何申请超限,限额为CommitLimit,超过CommitLimit的数值就会申请内存失败。
  • CommitLimit可在meminfo中查看,如下:
grep -i commit /proc/meminfo
CommitLimit:     6201492 kB
Committed_AS:    5770836 kB  //表示当前已经使用的内存
  • CommitLimit值由overcommit_ratio和swap大小决定,
CommitLimit = swap(交换内存) + mem(物理内存) * overcommit_ratio / 100
* overcommit_ratio 默认值为 50

理解

  • oom kill体现的是:应用层程序申请内存成功,但是运行时无法满足的情况。
  • 该机制是操作系统用来保证操作系统在内存不足情况下自身正常运行的一种机制。
  • 该机制会给进程评分,当系统可用物理内存低于内核预设阈值时,会kill掉一些评分较高的进程来释放内存,保证操作系统正常运行。
  • oom 机制的目的是保证操作系统的正常运行,不是保证程序的运行;如果没有oom机制,程序一直分配内存,可能会导致内核卡死,无响应,程序也无法运行,并且无法恢复。

触发条件

  • oom的触发条件是:系统无法给应用程序分配物理内存。
  • 无法分配到物理内存并不意味着物理内存已耗尽,也可能是其它缘由,如下:
  1. 内存划分过小,剩余内存都是内存碎片,无法分配一个稍大的内存。

物理内存阈值设置

  • 可通过以下接口设置阈值:
/proc/sys/vm/min_free_kbytes 
  • 该阈值是Linux最低剩余多少空闲物理内存(Kbytes)给内核使用;当可用物理内存低于这个参数时,系统触发缓存回收机制,以释放内存,直到可用物理内存大于这个值。
  • 该值不能设置的过小,原因如下:
  1. 当该值设置的过小,并且内存资源使用过量时,没有及时kill进程释放内存资源,如果设置了swap,当物理内存过低,降低内存页的命中率,也会导致内存频繁换页,频繁将内存页写入swap分区(磁盘),导致系统运行卡顿。
  2. 设置过小也可能会出现驱动加载时分配内存失败,无法加载驱动。
  3. 测试发现:过小会增加系统死机的概率,当内存太少,内核容易出现内存不足以处理异常事件,导致直接死机,无任何反应,例如:串口无任何输出。
  • 该值也不能设置过大,原因如下:
  1. 设置过大,会导致应用层能使用的物理内存减少,导致程序被kill掉。

oom和内存缓存

  • linux系统会将物理内存充当硬盘缓存来加快程序运行速度,因此内存缓存是否会诱发oom?
  • 我的回答是:如果缓存设置参数不合理,缓存会诱发oom问题,原因是:做安防摄像机产品时,产品循环录像(产生大量缓存,占用大量物理内存),导致剩余内存不多,测试时发现产品时常出现oom问题,后来通过内核参数对缓存大小进行了限制,剩余内存大大增加,oom问题也就没有出现了。
  • 总结:触发oom时,系统并不会去清空缓存以释放物理内存,因此未及时回写脏页,释放物理内存页,会导致内存不足,诱发oom问题的产生。

问题定位

  • 内核出现oom问题时,串口会打印oom信息,通过dmesg可查看崩溃时哪个进程的评分最高。

问题可能 - 曾经遇到该问题的最终原因

  1. 程序内存泄漏BUG,长时间运行后内存不足触发oom。
  • 内存泄漏一般都是每次泄漏一点点,问题重现需要时间长点,和运行的功能有关,可以通过关闭功能来确定范围,
  • 程序能够正常跑起来 一段时间后内核报 oom,看内存是不是一直在增长,如果在使用的过程中内存一直在增长,则很有可能是内存泄漏导致的。
  1. 嵌入式小内存设备,内存资源剩余不多,应用本身设计不当,某些功能会导致一次需要申请大量物理内存,或者其它常驻功能使用了大量的物理内存,再额外做些操作,导致随便使用多一些内存就出现oom。
  2. 嵌入式小内存设备,应用程序申请和释放过于频繁,并且内核中开启了内存碎片的整理,导致内核cpu占用越来越高,内存释放后内核来不及合并处理,导致内存越来越少,最终出现oom。
  • 需要时刻监控内存和cpu占用来分析原因。
  • 应用程序频繁申请和释放内存可能会导致内存碎片,当分配一个大点的内存时就会找不到,导致oom,内核有内存整理机制的,会将内存碎片消除掉,但是申请和释放过于频繁的话也会导致oom,并且cpu会不断升高。
  • 对于小内存设备,内存使用一定要计划好,并且至少要剩余一部分。

处理办法

单个进程保护方法

  1. 内核支持设置单个程序不被oom机制kill掉。
设置: echo -1000  /proc//oom_score_adj
oom_adj 接口已经过时了
/*
 * /proc//oom_score_adj set to OOM_SCORE_ADJ_MIN disables oom killing for
 * pid.
 */
#define OOM_SCORE_ADJ_MIN	(-1000)

测试方法

  1. 可以手动触发 oom 来测试程序保护和找到score最高进程
echo f > /proc/sysrq-trigger 

你可能感兴趣的:(#,Linux,内核知识)