因为在开发过程中遇到了OOM 所以想要信息理解一下这个OOM 到底是真么回事,然后就找到了这篇文章,为了自己学习,所以翻译一下,以备后面查看:
内存不足管理
我们要讨论的VM的最后一个方面是内存不足(OOM)管理器。这本章很短,因为它只有一个简单的任务。检查是否有足够的可用内存来满足需求,确认系统确实内存不足,如果是,请选择要终止的进程。这是VM中有争议的部分,建议将其删除很多次。不管它是否存在于最新的内核中,当它触发许多其他子系统时,它仍然是一个有用的系统。
检查可用内存
对于某些操作,例如使用brk()
扩展堆或使用mremap()
重新映射地址空间,系统将检查是否有足够的可用内存来满足请求。注意,这与下一部分介绍的out_of_memory()
路径分开。该路径用于尽可能避免系统处于OOM
状态。
检查可用内存时,所需的页数将作为参数传递到vm_enough_memory()
。除非系统管理员指定系统应过量使用内存,否则将检查可用内存的装载。为了确定潜在的页面数量,Linux
汇总了以下数据位:
Total page cache
总缓存页数:因为页面缓存很容易回收Total free pages
总空闲缓存页数:因为它们已经可用Total free swap pages
总空闲交换缓存页数:因为用户空间页面可能被换出Total pages managed by swapper_space
被swapper_space管理的总缓存页数:尽管这会重复计算空闲交换页。通过有时保留插槽但不使用插槽这一事实可以达到平衡(This is balanced by the fact that slots are sometimes reserved but not used)Total pages used by the dentry cache
dentry缓存使用的总页数:因为它们很容易回收Total pages used by the inode cache
索引节点高速缓存使用的总页数:因为它们很容易回收
如果此处添加的页面总数足以满足请求,则vm_enough_memory()
将true
返回给调用方。如果返回false
,则调用者知道该内存不可用,通常决定将-ENOMEM
返回给用户空间。
确定OOM状态
当机器内存不足时,将回收旧的页面框架(请参阅第10章),但是尽管回收了页面,但仍可能发现即使以最高优先级进行扫描,也无法释放足够的页面来满足请求。如果确实无法释放页面框架,则会调用out_of_memory()来查看系统是否内存不足并需要终止进程。
不幸的是,系统可能没有内存不足,仅需要等待IO
完成或将页面交换到后备存储即可。不幸的是,这不是因为系统具有内存,而是因为调用了该函数会不必要地打开不必要地终止进程的可能。在决定终止进程之前,它会通过以下清单。
- 是否还有足够的交换空间 (nr_swap_pages > 0) ? If yes, not OOM
- 自上次故障以来已经超过5秒了吗? If yes, not OOM
- 我们在最后一秒内失败了吗? If no, not OOM
- 如果至少在最近5秒钟内没有10次失败,we’re not OOM
- 最近5秒钟内进程是否被杀死? If yes, not OOM
只有通过以上测试,才调用oom_kill()
选择要杀死的进程。
选择过程
函数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
功能,则将这些点除以四,因为它们假定根特权进程行为良好。同样,如果它具有CAP_SYS_RAWIO
功能(访问原始设备)特权,则将这些点进一步除以4,因为不希望杀死直接访问硬件的进程。
杀死选定的进程
选择任务后,将再次遍历该列表,并且向与所选进程共享相同mm_struct
的每个进程(即它们是线程)发送信号。如果该进程具有CAP_SYS_RAWIO
功能,则发送SIGTERM
,使该进程有机会干净退出,否则发送SIGKILL
。
kernel 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
的内存量,这会增加一个称为commitd_space
的变量。释放VMA
后,使用vm_unacct_memory()
减少已提交的空间。这是一种相当简单的机制,但是它允许Linux
在决定是否应该提交更多的内存时记住它已经提交给用户空间的内存量。
通过调用security_vm_enough_memory()
来执行检查,这将向我们介绍另一个新功能。 2.6具有可用的功能,该功能允许与安全相关的内核模块覆盖某些内核功能。可用钩子的完整列表存储在称为security_ops
的结构security_operations
中。有许多可以使用的伪函数或缺省函数,它们都列在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页与SLAB_RECLAIM_ACCOUNT 集合:因为它们很容易回收
这些页面减去根进程的3%
保留空间,即请求可使用的内存总量。如果内存可用,则会进行检查以确保已提交的内存总量不超过允许的阈值。允许的阈值为TotalRam *(OverCommitRatio / 100)+ TotalSwapPage
,其中OverCommitRatio
由系统管理员设置。如果承诺的空间总量不太高,将返回1,以便可以进行分配。