Linux OOM killer机制介绍

2023-11-01

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      DMA
  pages free     3718
        min      335
        low      418
        high     502
……
        protection: (0, 3399, 3399)
……
Node 0, zone   Normal
  pages free     13719
        min      2224
        low      2780
        high     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 pages
  free_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 选择算法

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux OOM killer机制介绍 的相关文章

  • linux内核-系统调用execve()

    读者在linux内核 系统调用fork vfork与clone中已经看到 进程通常是按其父进程的原样复制出来的 在多数情况下 如果复制出来的子进程不能与父进程分道扬镳 走自己的路 那就没多大意义 所以 执行一个新的可执行程序是进程生命历程中
  • linux内核驱动中_IO, _IOR, _IOW, _IOWR 宏的用法与解析

    在驱动程序里 ioctl 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值 cmd除了可区别数字外 还包含有助于处理的几种相应信息 cmd的大小为 32位 共分 4 个域 bit31 bit30 2位为 区别读写 区
  • ARM 浮点运算详解

    一 早期ARM上的浮点模拟器 早期的ARM没有协处理器 所以浮点运算是由CPU来模拟的 即所需浮点运算均在浮点运算模拟器 float math emulation 上进行 需要的浮点运算 常要耗费数千个循环才能执行完毕 因此特别缓慢 直到今
  • linux的自旋锁struct spinlock_t的使用

    在linux中提供了一些机制用来避免竞争条件 最简单的一个种就是自旋锁 例如 当一个临界区的数据在多个函数之间被调用时 为了保护数据不被破坏 可以采用spinlock来保护临界区的数据 当然还有一个就是信号量也是可以实现临界区数据的保护的
  • linux的dirty page回写磁盘过程中是否允许并发写入更新page?

    概述 众所周知Linux内核write系统调用采用pagecache机制加速写入过程 避免write系统调用长时间block应用进程 用户态进程执行write调用的时候 内核只是将用户态buffer copy到内核的pagecache当中
  • linux源代码.tar.xz解压

    刚开始学习linux内核 在linux内核官网https www kernel org 下载 我下载的版本是 linux 2 6 34 14 tar xz 由于我的linux中没有安装 xz的解压缩软件 需要下载 http download
  • 第十三章 网络命名空间(内核源码实现)--基于Linux3.10

    下载地址 http download csdn net detail shichaog 8620701 有两篇翻译博文 Lxc之二 网络设置 和 linuxnamespace 之使用 LXC文章中关于网络的设置是从用户空间配置的 从该文章可
  • ARM架构内核启动分析-head.S(1.1、vmlinux.lds 链接脚本分析)

    ARM架构内核启动分析 一 start kernel之前 首先需要明确的是 内核镜像在被解压之后执行 是执行哪段代码 这是个重要的问题 平时在编译生成应用程序或内核模块时 我们无需考虑链接的具体细节 如代码和数据放在哪里 代码执行入口在哪等
  • 编译Linux内核时出现“ncurses-devel”错误

    通常在安装完Linux系统后 在编译kernel使用make menuconfig时 可能会出现如下错误 Unable to find the ncurses libraries or the required header files m
  • Linux read的核心函数generic_file_buffered_read

    内核 5 9 0 流程图 generic file buffered read一种调用路径 cat某个文件触发 0 ondemand readahead mapping 0xffff888005c61340 ra 0xffff8880059
  • netpoll浅析

    netpoll只是一种框架和一些接口 只有依赖这个框架和接口实现的netpoll实例 netpoll才能发挥它的功能 类似于kernel中的vfs vfs本身并不会去做具体的文件操作 只是为不同的文件系统提供了一个框架 netpoll不依赖
  • Linux提权之内核漏洞提权篇

    前言 在渗透过程中 有时利用某些漏洞可以获取一个低权限的用户 然后想办法提权 提升到root用户权限 从而控制整个系统 在获取到低权限shell后 通常会检查操作系统的发行版本 内核版本 老版本的系统可能会存在一些漏洞 于是我们可以利用这些
  • /etc/sysctl.conf 调优 & 优化Linux内核参数

    from http apps hi baidu com share detail 15652067 http keyknight blog 163 com blog static 3663784020104152407759 http bl
  • 第十四章 netlink机制--基于Linux3.10

    Netlink基于网络的消息机制 能够让用户和内核空间进行通信 12 3节提到的ifconfig是使用ioctl方法和内核通信的 而ip命令则是使用netlink和内核通信的 该机制初衷是为网络服务的 但是现在起应用范围已经大大扩展 14
  • file_operations 结构体

    file operations 结构体中的成员函数是字符设备驱动程序设计的主体内容 这些函数实际会在应用程序进行 Linux 的 open write read close 等系统调用时最终被调用 file operations 结构体目前
  • 一文搞懂Linux内核页框回收(Page Frame Reclamation)

    页替换策略 Page Replacement Policy 每当讨论页替换策略 提及最多的就是基于LRU Least Recently Used 的算法 但严格来说这是不对的因为这些lists并不是严格按照LRU的顺序来维护的 在Linux
  • RedmiBook pro15 2023款折腾笔记(7840HS)amd-pstat解决APU睿频问题

    这里记录一下RedmiBook pro15 2023款笔记本 7840HS 搭建ubuntu 22 04系统的记录 留给正好需要在这个笔记本上安装linux环境的童鞋参考一下 其他AMD的APU都是类似的 拿到机器过后安装ubuntu 22
  • 字符设备驱动相关函数

    Linux内核中 a 使用cdev结构体来描述字符设备 b 通过其成员dev t来定义设备号 分为主 次设备号 以确定字符设备的唯一性 c 通过其成员file operations来定义字符设备驱动提供给VFS的接口函数 如常见的open
  • Linux内核调度算法 -- CPU时间片如何分配

    内核在微观上 把CPU的运行时间分成许多分 然后安排给各个进程轮流运行 造成宏观上所有的进程仿佛同时在执行 双核CPU 实际上最多只能有两个进程在同时运行 大家在top vmstat命令里看到的正在运行的进程 并不是真的在占有着CPU哈 所
  • 内存文件系统提升磁盘性能瓶颈

    author skate time 2011 08 22 提升磁盘性能瓶颈 linux的内存文件系统 ramdisk ramfs tmpfs ramdisk 是块设备 在使用它们之前必须用选择文件系统将其格式化 并且调整文件系统大小比较麻烦

随机推荐

  • 开始在CSDN上安家了哈!

    2014年计划完成50 原创blog 这是我的目标
  • vue项目打包部署到tomcat(详细)

    hash路由模式打包部署到tomcat 1 修改config index js文件下的assetsPublicPath为 2 修改router文件夹下index js添加 base 文件夹名称 例如 yuncheng 可以自己随意设置 3
  • 未找到 van-toast 节点,请确认 selector 及 context 是否正确

    1 json文件引入 van toast vant weapp toast index 2 js文件引入 import Toast from vant weapp toast toast 3 wxml写入
  • 微信小程序蓝牙BLE开发实战——遇到问题及踩坑(三)

    微信小程序蓝牙BLE开发实战 三 对于我这种小白 遇到问题是常见的哈 这里记录下 避免日后再踩坑 文章目录 微信小程序蓝牙BLE开发实战 三 1 iPhone6及6plus无法搜索到设备 解决方案 2 IOS无法获取 mac 地址 如何连接
  • 分布式任务调度平台xxl-job

    一 java的集中式任务调度 while true Thread sleep 轮询 线程休眠的方式实现定时任务 java util Timer java util TimerTask Timer是一种定时器工具 用于使用后台线程计划执行指定
  • 数字IC设计流程学习笔记

    一 规格定制 IC的规格定制包括物理指标 性能指标和功能指标 物理指标 封装 工艺 芯片面积 性能指标 功耗 速度 功能指标 接口 芯片功能 二 系统设计 系统设计是确定IC的算法模型和系统架构等 并通过一些高级语言 matlab等对算法模
  • 【tensorflow基础】读取mnist数据

    转载于 MNIST手写数字数据集读取方法 TensorFlow的封装让使用MNIST数据集变得更加方便 MNIST数据集是NIST数据集的一个子集 它包含了60000张图片作为训练数据 10000张图片作为测试数据 在MNIST数据集中的每
  • spring-security

    文章目录 csrf remember me 密码存储 权限继承 应 要求添加的代码 白名单相关说明 csrf A网站登录 B网站 使用 Copyright C
  • 传染病模型(4)——SIRS模型和SIER模型及matlab具体程序

    前言 常见的传染病模型按照具体的传染病的特点可分为 SI SIS SIR SIRS SEIR 模型 其中 S E I R 的现实含义如下 S Susceptible 易感者 指缺乏免疫能力健康人 与感染者接触后容易受到感染 E Expose
  • 一文了解亚马逊云科技适用于 Amazon Lightsail 的托管数据库

    Amazon Lightsail 是亚马逊云科技提供的一种易上手使用 月度价格经济实惠 并包括了计算实例 容器 存储 数据库的虚拟专用服务器 在创建时可以进行业务蓝图选择 可选择包含多种操作系统 Linux Windows 等 或操作系统加
  • C++中定义常量的几种方式

    概述 在程序运行过程中 始终不发生改变的量 称之为常量 在 C 语言中常量是个固定值 也就是说常量值在定义后不能进行修改 define 宏常量 define 是 C 语言中定义常量的方式 在 C 中也可以使用 define 的使用 defi
  • RocketMQ安装与启动

    分享知识 传递快乐 官网 https rocketmq apache org 1 准备环境 系统 Centos7 jdk 1 8 2 环境部署 解压 rocketmq 并进入 rocketmq 下的 bin 目录 调整启动内存 vim bi
  • C++ 函数模板

    函数模板是通用的函数描述 它们使用泛型来定义函数 其中的泛型可用具体的类型替换 通过将类型作为参数传递给模板 可使编译器生成该类型的函数 由于模板允许以泛型 而不是具体类型 的方式编写程序 因此有时候也被称为通用编程 在标准C 98添加关键
  • ubuntu14.04安装wireshakes

    网络攻防 这课要做一个嗅探器的大作业 想在linux是实现 于是先在ubuntu上下一个wiresharks看看它的一些功能和 废话少说 直接上安装过程与期间遇到的问题 安装编译工具 sudo apt get install build e
  • Spring Gateway集成 Nacos注册中心不能够发现服务的问题解决

    一 问题描述 我们现在是在用Nacos替换Eureka 原来Eureka和Spring gateway运行正常 可以通过Spring gateway调用注册到Eureka中的服务 当前Spring cloud的版本是Hoxton SR8 N
  • 顺序表基本操作

    文章目录 1 顺序表插入元素 2 顺序表删除元素 3 顺序表查找元素 4 顺序表更改元素 1 顺序表插入元素 向顺序表中插入数据元素 根据插入位置的不同 可分为以下 3 种情况 插入到顺序表的表头 在表的中间位置插入元素 尾随顺序表中已有元
  • TCP/IP详解 卷1:协议 学习笔记 第十七章 TCP:传输控制协议

    TCP提供一种面向连接的 可靠的字节流服务 面向连接意味着两个使用TCP的应用 通常是一个客户一个服务器 在彼此交换数据前必须先建立一个TCP连接 在一个TCP连接中 仅有两方进行彼此通信 广播和多播不能用于TCP TCP提供可靠性的方法
  • Unity3D Animation、Animator和AnimationClip

    文章目录 Animation 字段 方法 Animator 字段 方法 AnimationClip 字段 方法 Animation 单一动画 一般使用在单一动画播放 占用资源小 字段 名称 作用 animatePhysics 如果打开这个选
  • qt中connect函数探究

    综合了一下网上资源 整理得出 QT 是一个跨平台的 C GUI 应用构架 它提供了丰富的窗口部件集 具有面向对象 易于扩展 真正的组件编程等特点 更为引人注目的是目前 Linux 上最为流行的 KDE 桌面环境就是建立在 QT 库的基础之上
  • Linux OOM killer机制介绍

    1 概念描述 Linux内核内存管理使用OOM killer Out Of Memory killer 机制 在系统内存不足时 选择性杀死一些进程以释放内存 以使系统继续运行 2 OOM killer产生的原因 2 1 malloc 内存分