Linux内核内存管理使用OOM killer(Out-Of-Memory killer)机制,在系统内存不足时,选择性杀死一些进程以释放内存,以使系统继续运行。
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返回非空指针并不代表指向内存就是可用的,而当系统没有内存时,会通过杀死进程来腾出内存。
此机制是指操作系统承诺给进程的内存大小超过实际可用物理内存。按照常规理解,内存分配有多少就分配多少,再申请就返回失败。但实际上这种策略是有些浪费内存,因为进程实际使用到的内存往往比申请的内存少。
按照Linux的算法,物理内存页的分配发生在使用瞬间,而不是在申请瞬间。overcommit针对 的也是内存申请,而不是内存分配。
Linux是允许memory overcommit的,即当申请的内存超过分配物理内存一定程度内,仍然可以申请成功。当实际使用时超过可分配物理内存时,利用OOM机制挑选一个进程出来杀死,以释放部分内存,如若不够则继续杀死进程,也可以配置内核参数panic_on_oom进行自动重启系统。
用以控制内核的overcommit策略开关。
可选值 | 含义 |
---|---|
OVERCOMMIT_GUESS(0) | 视情况允许overcommit。系统在为应用进程分配虚拟地址空间时,会判断当前申请的虚拟地址空间大小是否超过剩余内存大小,如果超过,则虚拟地址空间分配失败。 |
OVERCOMMIT_ALWAYS(1) | 总是允许overcommit。系统在为应用进程分配虚拟地址空间时,完全不进行限制,避免了fork可能产生的失败,但由于malloc是先分配虚拟地址空间,而后通过异常陷入内核分配真正的物理内存,在内存不足的情况下,完全屏蔽了应用进程对系统内存状态的感知,即malloc总是能成功,但内存不足时会引起系统OOM杀进程。 |
OVERCOMMIT_NEVER(2) | 禁止overcommit,根据系统内存状态确定虚拟地址空间上限。然而很多情况下,进程的虚拟地址空间占用远大于其实际占用的物理内存,这样一旦内存使用量上去以后,对于一些动态产生的进程(需要复制父进程地址空间)则很容易创建失败,如果业务过程没有过多的这种动态申请内存或者创建子进程,则影响不大,否则会产生比较大的影响 |
用以确定系统可申请内存大小,仅在禁止overcommit时生效。
可申请内存 = (总的物理内存 – huge页内存)* overcommit_ratio/100 + 交换分区大小。
系统为拥有cap_sys_admin权限用户预留的空闲内存大小。默认为3%的free pages与8MB中较小的值,以便管理员登录和杀死进程。
系统为普通用户预留的空闲内存大小,仅在禁止overcommit时生效,默认为3%的free pages与128MB中的较小值,以便用户登录和杀死进程。
/proc/meminfo信息,禁止禁止overcommit时生效。
参数名称 | 含义 |
---|---|
CommitLimit | 可申请内存 =(总的物理内存 – huge页内存)* overcommit_ratio /100 + 交换分区大小。 |
Commited_AS | 当前已申请内存。 |
用户空间分配内存时,内核都会调用__vm_enough_memory ()(mm/mmap.c) 来验证虚拟内存是否足够进行分配。
+=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,若空闲内存大于申请内存,返回内存充足,否则返回内存不足。
+=(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
已申请内存小于允许内存,返回内存充足,否则返回内存不足。
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,即跟内存大小成开方关系。
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会被使用,而满足分配申请。
在执行伙伴系统算法分配页框以前,需要调用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比较少,正好位于水印附近的时候。
根据上述说明,可以总结影响OOM发生的因素,请求分配的order大小,请求分配发生的zone,zone的水印大小,内存碎片化程度。
用以控制内核OOM时,是否触发panic的开关。
可选值 | 含义 |
---|---|
0 | 不触发panic。 |
1 | 如果是因为mempolicy、cpuset、memcg等限制未分配到内存,实际还有内存那么不触发panic,否则触发panic。 |
2 | 直接触发panic。 |
用以控制内核OOM时,是否调用dump_tasks来打印所有task的内存状况。
可选值 | 含义 |
---|---|
0 | 不会调用打印(进程数量很多时,逐一打印可能导致性能问题)。 |
1 | 在如下情况会执行打印:a.由于OOM导致kernel panic,b.没有找到合适的bad进程,c.找到合适进程杀死时。 |
用以控制内核OOM时,是否优先杀死触发OOM的进程。
可选值 | 含义 |
---|---|
0 | 选择最“坏”进程杀死。 |
1 | 若触发OOM进程是用户空间进程、不是unkillable task(如init进程),未设定oom_score_adj阻止kill该进程,则直接杀死此进程。 |
进程得分相关参数,每个进程独有。
参数名称 | 含义 |
---|---|
oom_score | 进程oom_badness计算得分,为只读参数。 |
oom_score_adj | 计算进程得分时的调整值。取值范围是-1000~1000,0表示不调整得分,负值表示在实际打分值上减去一个折扣,正值表示要惩罚该进程增加得分。 |
oom_adj | 旧的计算进程得分时的调整值,实际使用时会转换为oom_score_adj。 |
本文发布于:2024-02-02 04:50:23,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170682062241467.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |