Linux内核-内存管理: Out Of Memory Management - OOM

Posted by LB on Sat, Mar 7, 2020

一. 引言

这篇文章,我们将要讨论的是内存不足(OOM)管理器,OOM检查是否有足够的可用内存来满足系统运行需求,如果没有足够的可用内存则进行进程kill操作。这是Linux内核虚拟内存模块中一个有争议的部分,有人建议在很多情况下删除它。所以在使用OOM的时候,首先需要确认OOM是否在待操作的Linux内核中存在,还需要确定OOM在Linux内核中的开启与关闭选项。

二. 检查可用内存(Checking Available Memory)

对于某些操作,例如使用brk()扩展堆或使用mremap()重新映射地址空间,系统将检查是否有足够的可用内存来满足请求。请注意,这与当前文章后面介绍的out_of_memory()是不同部分,Linux内核的内存分配机制和内存可用度检查用于尽可能避免系统处于OOM状态。

检查可用内存时,所需的页数作为参数传递给vm_enough_memory()。除非系统管理员指定系统应超量使用内存,否则将检查可用内存的装载。为了确定有多少页面是可用的,Linux总结了以下数据位:

  • Total page cache (总页面缓存):页面缓存很容易回收。
  • Total free pages (总空闲页面):总可用页面,它们已经可用。
  • Total free swap pages(总自由交换页) :用户空间页面可能会被换出。
  • Total pages managed by swapper_space(由swapper_space管理的总页面): 尽管这将重复计算空闲交换页面。这是平衡某些情况,此选项有时保留但不使用。
  • Total pages used by the dentry cache(dentry缓存使用的总页面): 这部分内存很容易被回收,主要用于vfs
  • Total pages used by the inode cache (inode缓存使用的总页面): 这部分内存很容易被回收,主要用于vfs-inode索引

如果在此处添加的页面总数足以满足请求,则vm_enough_memory() 将true返回给调用方。 如果返回false,则调用者知道该内存不可用,通常决定将-ENOMEM返回给用户空间。

三. 确定OOM状态(Determining OOM Status)

当机器内存不足时,将回收旧的页面框架,但是尽管回收了页面,但仍可能发现即使以最高优先级进行扫描,也无法释放足够的页面来满足请求。如果无法释放页帧,则会调用out_of_memory() 以查看系统是否内存不足,是否需要终止进程。

不幸的是,系统可能没有内存不足,只需要等待IO完成或页面交换到外部存储。不幸的是,这不是因为系统具有内存,而是因为这个函数被不必要地调用,导致不必要地关闭进程。在决定终止一个进程之前,它要经过以下检查表。

  • 是否还有足够的交换空间(nr_swap_pages> 0) ?如果,则不进行OOM
  • 从上次失败到现在已经超过5秒了吗?如果,则不进行OOM
  • 我们在最后一秒失败了吗?如果没有,则不进行OOM
  • 如果在过去5秒内没有10次失败,就不进行OOM
  • 最近5秒钟内进程是否被杀死? 如果,则不进行OOM

四. 选择一个进程(Selecting a Process)

函数select_bad_process()负责选择要终止的进程。它通过逐步执行每个正在运行的任务并计算使用badness()函数杀死它的适合程度来做出决定。坏度的计算方法如下,请注意,平方根是使用int_sqrt()计算的整数近似值。

1badness_for_task = total_vm_for_task / (sqrt(cpu_time_in_seconds) *
2sqrt(sqrt(cpu_time_in_minutes)))

这个公式是为了选择一个使用大量内存但寿命不是很长的进程。已经运行了很长时间的进程不太可能是导致内存不足的原因,因此这个计算可能会选择一个使用了大量内存但没有运行很长时间的进程。如果该进程是根进程或具有CAP_SYS_ADMIN功能,则将坏度值除以4,因为假定根特权进程表现良好。类似地,如果它具有CAP_SYS_RAWIO功能(访问原始设备)特权,则坏度值进一步除以4,因为不希望杀死一个直接访问硬件的进程。

五. 终止选定的进程

一旦选择了一个任务,就会再次遍历该列表,并且每个与所选进程(即它们是线程)共享相同mm_struct的进程都会被发送一个信号。如果进程具有CAP_SYS_RAWIO功能,则发送SIGTERM以使进程有机会干净地退出,否则发送SIGKILL。

六. 2.6内核的新特性

除了引入了VM记账对象外,2.6版本的大多数OOM管理基本上保持不变。设置了此标志的VMAs上执行操作时,将进行其他检查以确保内存可用。这种复杂性的主要动机是为了避免使用OOM杀手。

总是设置VM_ACCOUNT标志的区域包括进程堆栈、进程堆、带有MAP_SHARED的区域mmap()、可写的私有区域和设置shmget()的区域。换句话说,大多数用户空间映射都设置了VM_ACCOUNT标志。

Linux使用vm_acct_memory()占提交给这些VMA的内存量,这会增加一个称为commitd_space的变量。 释放VMA后,使用vm_unacct_memory()减少已提交的空间。 这是一种相当简单的机制,但是它允许Linux在决定是否应该提交更多的内存时记住它已经提交给用户空间的内存量。

通过调用security_vm_enough_memory()来执行检查,这将为我们引入另一个新特性。2.6有一个可用的特性,允许与安全相关的内核模块覆盖某些内核函数。可用钩子的完整列表存储在名为security-ops的struct security-ops中。 可以使用许多伪函数或缺省函数,这些函数都列在security / dummy.c中,但是大多数函数除了return之外什么都不做。 如果没有加载安全模块,则使用的security_operations结构称为dummy_security_ops,它使用所有默认功能。

默认情况下,security_vm_enough_memory()调用dummy_vm_enough_memory(),该函数在security/dummy.c中声明,非常类似于2.4的vm_enough_memory()函数。新版本将以下信息添加在一起以确定可用内存:

  • Total page cache(总页面缓存):这部分内存很容易被回收
  • Total free pages (总空闲页面):这部分内存是可用的
  • Total free swap pages(总自由交换页):用户空间页面可能会被换出
  • Slab pages with SLAB_RECLAIM_ACCOUNT set (设置了SLAB_RECLAIM_ACCOUNT的Slab页面):这部分内存很容易被回收

这些页面减去3%的根进程保留,就是请求可用的内存总量。如果内存可用,则进行检查以确保提交的内存总量不超过允许的阈值。允许的阈值是TotalRam * (overcommratio/100) + TotalSwapPage,其中overcommratio由系统管理员设置。如果提交的空间总量不太高,将返回1以便继续分配。

Original URL : https://www.kernel.org/doc/gorman/html/understand/understand016.html