我们要讨论的VM的最后一个方面是内存不足(OOM)管理器。这故意是一个非常简短的章节,因为它有一个简单的任务; 检查是否有足够的可用内存来满足,验证系统是否真的没有内存,如果是,请选择要杀死的进程。这是虚拟机的一个有争议的部分,有人建议它在很多场合被删除。无论它是否存在于最新的内核中,它仍然是一个有用的系统,因为它触及了许多其他子系统。

13.1   检查可用内存


对于某些操作,例如使用brk()覆盖堆或使用mremap()重新映射地址空间,系统将检查是否有足够的可用内存来满足请求。请注意,这与下一节中介绍的out_of_memory()路径是分开的。该路径用于避免系统处于OOM状态(如果可能的话)。

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


页面缓存总页面缓存

很容易回收 可用页面总数,

因为它们已经可用

可以调出用户空间页面的 总免费交换页面

swapper_space

管理的总页数尽管这会对可用交换页进行双重计算。这是因为插槽有时被保留但未被使用

dentry缓存使用的总页数,

因为它们很容易回收 inode缓存使用的总页数,

因为它们很容易回收

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

13.2   确定OOM状态


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



vmmem内存设置_用户空间

图13.1:调用图:out_of_memory()



不幸的是,系统可能没有内存,只需要等待IO完成或者将页面交换到后备存储。这是不幸的,不是因为系统有内存,而是因为正在调用该函数而不必要地打开可能不必要地杀死的进程。在决定终止进程之前,它会通过以下清单。

  • 是否有足够的交换空间(nr_swap_pages > 0)?如果是,不是OOM
  • 自上次失败以来已经超过5秒了吗?如果是,不是OOM
  • 我们在最后一秒内失败了吗?如果不是,不是OOM
  • 如果至少在最后5秒内没有10次失败,我们就不是OOM
  • 过程中是否有过程被杀?如果是,不是OOM

只有在上面的测试通过oom_kill() 被调用以选择要杀死的进程。

13.3   选择流程


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

badness_for_task = total_vm_for_task /(sqrt(cpu_time_in_seconds)*
SQRT(SQRT(cpu_time_in_minutes)))

选择此选项是为了选择使用大量内存但过程不长的进程。已经运行很长时间的进程不太可能是内存不足的原因,因此这种计算可能会选择一个使用大量内存但运行时间不长的进程。如果进程是根进程或具有 CAP_SYS_ADMIN功能,则将这些点除以4,因为假定根权限进程表现良好。同样,如果它具有CAP_SYS_RAWIO功能(访问原始设备)权限,则点将进一步除以4,因为不希望终止可直接访问硬件的进程。

13.4   杀死所选进程


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

13.5   是吗?

是的,就是这样,内存管理无法触及很多子系统,否则就没有多少了。

13.6   2.6中的新功能

除了引入VM占用对象之外,大多数OOM管理在2.6中基本保持不变。这些是使用VM_ACCOUNT标记的VMA标志,在4.8节中首次提到 。将进行附加检查以确保在设置此标志的VMA上执行操作时可用内存。这种复杂性的主要动机是避免使用OOM杀手。

一些始终具有VM_ACCOUNT标志集的区域是进程堆栈,进程堆,使用MAP_SHARED编辑的 区域mmap(),可写入的专用区域以及设置shmget()的区域。换句话说,大多数用户空间映射都设置了 VM_ACCOUNT标志。

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

通过调用security_vm_enough_memory()来执行检查 它向我们介绍了另一个新功能。2.6具有可用的功能,允许安全相关的内核模块覆盖某些内核功能。可用挂钩的完整列表存储在struct security_operations中叫做 security_ops。可以使用许多虚拟或默认函数,这些函数都在security / dummy.c中列出,但除了返回之外,大多数都不执行任何操作。如果没有加载安全模块,则使用的security_operations结构称为 dummy_security_ops 它使用所有默认功能。

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


页面缓存总页面缓存

很容易回收 可用页面总数,

因为它们已经可用

可以调出用户空间页面的 总免费交换页面

设置了

SLAB_RECLAIM_ACCOUNT的

平板页面,因为它们很容易回收


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