Linux OOM killer机制介绍

2023-12-19 08:40
文章标签 linux 介绍 机制 killer oom

本文主要是介绍Linux OOM killer机制介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 概念描述

Linux内核内存管理使用OOM killer(Out-Of-Memory killer)机制,在系统内存不足时,选择性杀死一些进程以释放内存,以使系统继续运行。

2. OOM killer产生的原因

2.1 malloc 内存分配

By default, Linux follows an optimistic memory allocation strategy.
This means that when malloc() returns non-NULL there is no guarantee that the memory really is available. This is a really bad bug. In case it turns out that the system is out of memory, one or more processes will be killed by the infamous OOM killer.

如上为malloc的manpage节选,说明malloc返回非空指针并不代表指向内存就是可用的,而当系统没有内存时,会通过杀死进程来腾出内存。

2.2 Memory Overcommit

此机制是指操作系统承诺给进程的内存大小超过实际可用物理内存。按照常规理解,内存分配有多少就分配多少,再申请就返回失败。但实际上这种策略是有些浪费内存,因为进程实际使用到的内存往往比申请的内存少。
按照Linux的算法,物理内存页的分配发生在使用瞬间,而不是在申请瞬间。overcommit针对 的也是内存申请,而不是内存分配。
Linux是允许memory overcommit的,即当申请的内存超过分配物理内存一定程度内,仍然可以申请成功。当实际使用时超过可分配物理内存时,利用OOM机制挑选一个进程出来杀死,以释放部分内存,如若不够则继续杀死进程,也可以配置内核参数panic_on_oom进行自动重启系统。

3. 虚拟内存分配

3.1 影响参数

3.1.1 overcommit_memory

用以控制内核的overcommit策略开关。

可选值含义
OVERCOMMIT_GUESS(0)视情况允许overcommit。系统在为应用进程分配虚拟地址空间时,会判断当前申请的虚拟地址空间大小是否超过剩余内存大小,如果超过,则虚拟地址空间分配失败。
OVERCOMMIT_ALWAYS(1)总是允许overcommit。系统在为应用进程分配虚拟地址空间时,完全不进行限制,避免了fork可能产生的失败,但由于malloc是先分配虚拟地址空间,而后通过异常陷入内核分配真正的物理内存,在内存不足的情况下,完全屏蔽了应用进程对系统内存状态的感知,即malloc总是能成功,但内存不足时会引起系统OOM杀进程。
OVERCOMMIT_NEVER(2)禁止overcommit,根据系统内存状态确定虚拟地址空间上限。然而很多情况下,进程的虚拟地址空间占用远大于其实际占用的物理内存,这样一旦内存使用量上去以后,对于一些动态产生的进程(需要复制父进程地址空间)则很容易创建失败,如果业务过程没有过多的这种动态申请内存或者创建子进程,则影响不大,否则会产生比较大的影响

3.1.2 overcommit_ratio

用以确定系统可申请内存大小,仅在禁止overcommit时生效。
可申请内存 = (总的物理内存 – huge页内存)* overcommit_ratio/100 + 交换分区大小。

3.1.3 admin_reserve_kbytes

系统为拥有cap_sys_admin权限用户预留的空闲内存大小。默认为3%的free pages与8MB中较小的值,以便管理员登录和杀死进程。

3.1.4 user_reserve_kbytes

系统为普通用户预留的空闲内存大小,仅在禁止overcommit时生效,默认为3%的free pages与128MB中的较小值,以便用户登录和杀死进程。

3.1.5 CommitLimit 和 Commited_AS

/proc/meminfo信息,禁止禁止overcommit时生效。

参数名称含义
CommitLimit可申请内存 =(总的物理内存 – huge页内存)* overcommit_ratio /100 + 交换分区大小。
Commited_AS当前已申请内存。

3.2 内存充足判断

用户空间分配内存时,内核都会调用__vm_enough_memory ()(mm/mmap.c) 来验证虚拟内存是否足够进行分配。

  1. 总是允许overcommit,直接返回0,表示内存充足。
  2. 视情况判断overcommit
    空闲内存free page计算,如下。
+=global_page_state(NR_FREE_PAGES)
位于Buddy system的free list中,没有任何开销可以使用;+=global_page_state(NR_FILE_PAGES)
page cache,为加快用户空间读写文件性能使用,可以直接操作磁盘,可视为free。-=global_page_state(NR_SHMEM)
用于进程间share memory机制,不能清除free,因此要减去。+= get_nr_swap_pages()
swap file或swap device上的空闲page frame,由于其可作为anonymous page做腾挪,即把使用中的page frame swap out到swap page上,也算作free,开销较大。+= global_page_state(NR_SLAB_RECLAIMABLE)
slab已标记可回收的page frame。-= totalreserve_pages
系统运行预留的page,若小于等于此直接返回内存不足,否则减去预留。-= sysctl_admin_reserve_kbytes >> (PAGE_SHIFT - 10)
对于普通进程,保留一定的free page,以保证管理员可以登录并执行恢复操作。free > pages,若空闲内存大于申请内存,返回内存充足,否则返回内存不足。
  1. 禁止overcommit
    可申请内存allowed阈值计算,如下。
+=(totalram_pages - hugetlb_total_pages()) * sysctl_overcommit_ratio / 100。-= sysctl_admin_reserve_kbytes >> (PAGE_SHIFT - 10)
对于普通用户进程,保留一定的free page,以保证管理员可以登录并执行恢复操作。-= min(mm->total_vm / 32, sysctl_user_reserve_kbytes >> (PAGE_SHIFT - 10)
用户态进程,需预留内存保证登录并执行恢复操作。percpu_counter_read_positive(&vm_committed_as) < allowed
已申请内存小于允许内存,返回内存充足,否则返回内存不足。

4. 物理内存分配

4.1 影响参数

4.1.1 min_free_kbytes

  1. 代表系统所保留空闲内存的最低限。
    min_free_kbytes = sqrt(lowmem_kbytes * 16) = 4 * sqrt(lowmem_kbytes)
    最小为128K,最大为64M ,lowmem_kbytes可认为是系统内存大小。
  2. 用于计算内存回收水印(zone_watermark)。
  1. zone watermark计算方法(每个zone各有一套watermark参数)
watermark[min] = per_zone_min_free_pages (min_free_kbytes换算为page单位)
watermark[low] = watermark[min] * 5 / 4
watermark[high] = watermark[min] * 3 / 2
不同内存水印跨度:per_zone_min_free_pages * 1/4,即跟内存大小成开方关系。
  1. zone_watermark作用
    在系统空闲内存低于low时,开始启动内核线程kswapd进行内存回收,直到该zone的空闲内存数量达到high后停止回收。
    如果上层申请内存的速度太快,导致空闲内存降至min后,内核就会进行direct reclaim(直接回收),即直接在应用程序的进程上下文中进行回收,再用回收上来的空闲页满足内存申请,因此实际会阻塞应用程序,带来一定的响应延迟,而且可能会触发系统OOM。
    min以下的内存属于系统自留内存,用以满足特殊使用,不会给用户态的普通申请来用。
  1. min_free_kbytes大小的影响
    min_free_kbytes越大,watermark越高,同时三个线之间的buffer量也会相应增加。这意味着会较早的启动kswapd进行回收,且会回收上来较多的内存(直至watermark[high]才会停止),导致系统预留过多空闲内存,一定程度上降低了应用程序可使用的内存。极端情况下设置为接近内存大小时,留给应用程序的内存就会太少而可能频繁导致OOM的发生。
    min_free_kbytes过小,则会导致系统预留内存过小。kswapd回收的过程中也会有少量的内存分配行为(会设上PF_MEMALLOC)标志,这个标志会允许kswapd使用预留内存;另外一种情况是被OOM选中杀死的进程在退出过程中,如果需要申请内存也可以使用预留部分。这两种情况下使用预留内存可以避免系统进入deadlock状态。

4.1.2 lowmem_reserve_ratio

  1. 各个zone之间进行的内存预留
    防止高端zone在没内存的情况下过度使用低端zone的内存资源。低端内存比较小,且有一定的特殊作用比如发生DMA时只能分配DMA zone的低端内存,因此需要在尽量可以使用高端内存时 而不使用低端内存,同时防止高端内存分配不足的时候抢占稀有的低端内存。此参数可以调整内核对于lower zone的保护力度。
  2. 计算及使用方法
    lowmem_reserve_ratio是一个数组,可以通过以下命令查看。
cat /proc/sys/vm/lowmem_reserve_ratio
256     32

数组的长度=内存zone数量 - 1,其中每个数并不是绝对值,而是一个比例,代表1/256或1/32。zone[i] 的 protection[j] 计算规则如下。

(i < j):
zone[i]->protection[j] = (total sums of present_pages from zone[i+1] to zone[j] on the node)/ lowmem_reserve_ratio[i];
(i = j):(should not be protected. = 0;
(i > j):(not necessary, but looks 0)

计算结果可通过以下命令查看。

cat /proc/zoneinfo
Node 0, zone      DMApages free     3718min      335low      418high     502
……protection: (0, 3399, 3399)
……
Node 0, zone   Normalpages free     13719min      2224low      2780high     3336
……protection: (0, 0, 0)
……

在进行内存分配时,这些预留页数和watermark相加来一起决定现在是满足分配请求,若是认为空闲内存量过低需要启动回收。例如,如果一个normal区的页申请来试图分配DMA区的内存,且现在使用的判断标准是watermark[low]时,内核计算出 page_free = 3718,而watermark + protection[1] = 418 + 3399 = 3817 > page_free,则认为空闲内存太少而不予以分配。如果分配请求本就来自DMA zone,则 protection[0] = 0会被使用,而满足分配申请。

4.2 内存充足判断

在执行伙伴系统算法分配页框以前,需要调用zone_watermark_ok()(mm/page_alloc.c)判断当前内存区中的页框数目是否满足水印要求。若不满足要求,则会分配失败,最终导致OOM。

min = mark; 根据水印设置最小值
if (alloc_flags & ALLOC_HIGH) min -= min / 2; 若需求比较迫切,则放宽限制
if (alloc_flags & ALLOC_HARDER) min -= min / 4; 若需求很迫切,则进一步放宽限制free_pages = zone_page_state(z, NR_FREE_PAGES); 计算空闲页面数量
free_pages -= (1 << order) - 1;
判断的是分配出去2^order个page之后的free pages是否满足水印要求free_pages <= min + z->lowmem_reserve[classzone_idx]; 
若free pages已经不大于保留内存和min之和,说明不满足watermark要求遍历buddy中比请求order小的所有order,检查free pages是否满足watermark要求。
for (o = 0; o < order; o++) {每个循环当中,先减去当前order的free pagesfree_pages -= z->free_area[o].nr_free << o;将min,即判断标准作相应的缩小min >>= 1;比较处理后的free pages和min,看是否满足watermark要求if (free_pages <= min)return false;
}

简单来看分配完后只要剩余的页数大于水位,就可以安然返回。但是伙伴系统不仅考虑剩余也总数,还要考虑内存碎片情况,上述检查循环其实是为了保证位于高阶和低阶的页大体均衡。尤其是free_pages比较少,正好位于水印附近的时候。

4.3 OOM 发生的影响因素

根据上述说明,可以总结影响OOM发生的因素,请求分配的order大小,请求分配发生的zone,zone的水印大小,内存碎片化程度。

5. OOM killer 调试参数

5.1 panic_on_oom

用以控制内核OOM时,是否触发panic的开关。

可选值含义
0不触发panic。
1如果是因为mempolicy、cpuset、memcg等限制未分配到内存,实际还有内存那么不触发panic,否则触发panic。
2直接触发panic。

5.2 oom_dump_tasks

用以控制内核OOM时,是否调用dump_tasks来打印所有task的内存状况。

可选值含义
0不会调用打印(进程数量很多时,逐一打印可能导致性能问题)。
1在如下情况会执行打印:a.由于OOM导致kernel panic,b.没有找到合适的bad进程,c.找到合适进程杀死时。

5.3 oom_kill_allocating_task

用以控制内核OOM时,是否优先杀死触发OOM的进程。

可选值含义
0选择最“坏”进程杀死。
1若触发OOM进程是用户空间进程、不是unkillable task(如init进程),未设定oom_score_adj阻止kill该进程,则直接杀死此进程。

5.4 oom_score_adj

进程得分相关参数,每个进程独有。

参数名称含义
oom_score进程oom_badness计算得分,为只读参数。
oom_score_adj计算进程得分时的调整值。取值范围是-1000~1000,0表示不调整得分,负值表示在实际打分值上减去一个折扣,正值表示要惩罚该进程增加得分。
oom_adj旧的计算进程得分时的调整值,实际使用时会转换为oom_score_adj。

6. OOM killer 选择算法

在这里插入图片描述

这篇关于Linux OOM killer机制介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/511561

相关文章

Linux下修改hostname的三种实现方式

《Linux下修改hostname的三种实现方式》:本文主要介绍Linux下修改hostname的三种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下修改ho编程stname三种方式方法1:修改配置文件方法2:hFvEWEostnamectl命

Linux虚拟机不显示IP地址的解决方法(亲测有效)

《Linux虚拟机不显示IP地址的解决方法(亲测有效)》本文主要介绍了通过VMware新装的Linux系统没有IP地址的解决方法,主要步骤包括:关闭虚拟机、打开VM虚拟网络编辑器、还原VMnet8或修... 目录前言步骤0.问题情况1.关闭虚拟机2.China编程打开VM虚拟网络编辑器3.1 方法一:点击还原VM

Linux搭建Mysql主从同步的教程

《Linux搭建Mysql主从同步的教程》:本文主要介绍Linux搭建Mysql主从同步的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux搭建mysql主从同步1.启动mysql服务2.修改Mysql主库配置文件/etc/my.cnf3.重启主库my

JAVA SE包装类和泛型详细介绍及说明方法

《JAVASE包装类和泛型详细介绍及说明方法》:本文主要介绍JAVASE包装类和泛型的相关资料,包括基本数据类型与包装类的对应关系,以及装箱和拆箱的概念,并重点讲解了自动装箱和自动拆箱的机制,文... 目录1. 包装类1.1 基本数据类型和对应的包装类1.2 装箱和拆箱1.3 自动装箱和自动拆箱2. 泛型2

Linux系统之authconfig命令的使用解读

《Linux系统之authconfig命令的使用解读》authconfig是一个用于配置Linux系统身份验证和账户管理设置的命令行工具,主要用于RedHat系列的Linux发行版,它提供了一系列选项... 目录linux authconfig命令的使用基本语法常用选项示例总结Linux authconfi

jdk21下载、安装详细教程(Windows、Linux、macOS)

《jdk21下载、安装详细教程(Windows、Linux、macOS)》本文介绍了OpenJDK21的下载地址和安装步骤,包括Windows、Linux和macOS平台,下载后解压并设置环境变量,最... 目录1、官网2、下载openjdk3、安装4、验证1、官网官网地址:OpenJDK下载地址:Ar

linux本机进程间通信之UDS详解

《linux本机进程间通信之UDS详解》文章介绍了Unix域套接字(UDS)的使用方法,这是一种在同一台主机上不同进程间通信的方式,UDS支持三种套接字类型:SOCK_STREAM、SOCK_DGRA... 目录基础概念本机进程间通信socket实现AF_INET数据收发示意图AF_Unix数据收发流程图A

linux环境openssl、openssh升级流程

《linux环境openssl、openssh升级流程》该文章详细介绍了在Ubuntu22.04系统上升级OpenSSL和OpenSSH的方法,首先,升级OpenSSL的步骤包括下载最新版本、安装编译... 目录一.升级openssl1.官网下载最新版openssl2.安装编译环境3.下载后解压安装4.备份

Nginx之upstream被动式重试机制的实现

《Nginx之upstream被动式重试机制的实现》本文主要介绍了Nginx之upstream被动式重试机制的实现,可以通过proxy_next_upstream来自定义配置,具有一定的参考价值,感兴... 目录默认错误选择定义错误指令配置proxy_next_upstreamproxy_next_upst

linux打包解压命令方式

《linux打包解压命令方式》文章介绍了Linux系统中常用的打包和解压命令,包括tar和zip,使用tar命令可以创建和解压tar格式的归档文件,使用zip命令可以创建和解压zip格式的压缩文件,每... 目录Lijavascriptnux 打包和解压命令打包命令解压命令总结linux 打包和解压命令打