发布时间:2023-07-07 19:30
在日常的工作中可能我们会经常遇到程序fork失败的问题。遇到fork失败往往有两种可能性:
cat /proc/sys/kernel/threads-max
查看进程数预设值往往很大,几乎不太能超标,所以fork失败的原因大部分都是由于内存不足造成的。
我们知道,由于MMU实现了虚拟地址到物理地址的转换,所以我们在申请虚拟地址时往往可以申请一大块内存,这实际上是对资源的有效利用,毕竟只有内存真正被投入使用时(如memset)才会实际分配物理内存,这种允许内存超额commit的机制就是overcommit_memory。
虚拟内存需要物理内存作为支撑,当分配了太多虚拟内存,导致物理内存不够时,就发生了Out Of Memory。这种允许超额commit的机制就是overcommit。
从图中我们可以看到,fork一步一步执行到了__vm_enough_memory
,该接口是overcommit的核心。
Linux根据参数vm.overcommit_memory
设置overcommit:
overcommit
,对内存申请来者不拒。overcommit
,提交给系统的总地址空间大小不允许超过。include/uapi/linux/mman.h
中声明了该宏定义:
#define OVERCOMMIT_GUESS 0
#define OVERCOMMIT_ALWAYS 1
#define OVERCOMMIT_NEVER 2
当sysctl_overcommit_memory
等于OVERCOMMIT_ALWAYS
时,内核的处理方式是直接返回。
if (sysctl_overcommit_memory == OVERCOMMIT_ALWAYS)
return 0;
当sysctl_overcommit_memory
等于OVERCOMMIT_GUESS
时,主要代码如下:
if (sysctl_overcommit_memory == OVERCOMMIT_GUESS) {
free = global_page_state(NR_FREE_PAGES);
free += global_node_page_state(NR_FILE_PAGES);
free -= global_node_page_state(NR_SHMEM);
free += get_nr_swap_pages();
free += global_page_state(NR_SLAB_RECLAIMABLE);
if (free <= totalreserve_pages)
goto error;
else
free -= totalreserve_pages;
if (!cap_sys_admin)
free -= sysctl_admin_reserve_kbytes >> (PAGE_SHIFT - 10); /* 为root保留的页数 */
if (free > pages)
return 0;
goto error;
}
其中global_page_state
负责读取vm_zone_stat数组中的各项值。
extern atomic_long_t vm_zone_stat[NR_VM_ZONE_STAT_ITEMS];
NR_FREE_PAGES
:富足的未分配资源;NR_FILE_PAGES
:page cache总和;NR_SHMEM
:进程间的share memory;NR_SLAB_RECLAIMABLE
:slab可回收的页数;totalreserve_pages
:每个zone保留的不能被用户空间分配的页数;get_nr_swap_pages
:针对空闲的swap页数所以free的计算公式如下:
free = NR_FREE_PAGES + NR_FILE_PAGES - NR_SHMEM + swap_pages + NR_SLAB_RECLAIMABLE
如果此时free小于totalreserve_pages
,则判定内存不足,否则 free -= totalreserve_pages
.
如果是普通进程,则还需要保留admin_reserve_kbytes
(/proc/sys/vm/admin_reserve_kbytes)的free page.
此时free若大于申请的pages,则不会发生overcommit。
当sysctl_overcommit_memory
等于OVERCOMMIT_NEVER
时,相关代码如下:
allowed = vm_commit_limit();
if (!cap_sys_admin)
allowed -= sysctl_admin_reserve_kbytes >> (PAGE_SHIFT - 10);
if (mm) {
reserve = sysctl_user_reserve_kbytes >> (PAGE_SHIFT - 10);
allowed -= min_t(long, mm->total_vm / 32, reserve);
}
if (percpu_counter_read_positive(&vm_committed_as) < allowed)
return 0;
error:
vm_unacct_memory(pages);
return -ENOMEM;
/
unsigned long vm_commit_limit(void)
{
unsigned long allowed;
if (sysctl_overcommit_kbytes)
allowed = sysctl_overcommit_kbytes >> (PAGE_SHIFT - 10);
else
allowed = ((totalram_pages - hugetlb_total_pages())
* sysctl_overcommit_ratio / 100);
allowed += total_swap_pages;
return allowed;
}
主要看如下值:
sysctl_overcommit_kbytes
:/proc/sys/vm/overcommit_kbytes的值sysctl_overcommit_ratio
:/proc/sys/vm/overcommit_ratio的值totalram_pages
:可分配的总页数total_swap_pages
:可换出的页数hugetlb_total_pages
:huge page相关sysctl_user_reserve_kbytes
:/proc/sys/vm/user_reserve_kbytes的值,用户空间保留的内存大小vm_committed_as
:保存了当前系统中已经申请的虚拟内存大小由代码可知,vm_commit_limit
包括两种情况:
overcommit_kbytes
不为0, allow值为overcommit_kbytes
的一半;overcommit_kbytes
为0,allow值为可管理的总内存与vercommit_ratio
的乘积百分比那么如何设置overcommit的值呢?有三种方法:
本文通过分析内核代码粗略得分析了overcommit_memory的原理,也为后面的OOM-killer的原理做个铺垫。
斯坦福NLP名课带学详解 | CS224n 第6讲 - 循环神经网络与语言模型(NLP通关指南·完结)
逆向分析工具IDA与开源工具Ghidra、Cutter对比测评
【mindspore】【算子不支持GPU】Unsupported op [Ceil] on GPU
Linux—用户新建目录和文件的默认权限设置:umask详解
windows11系统中使用VMware蓝屏报错:终止代码:SYSTEM_SERVICE_EXCEPTION
985北京航空航天大学软件考研改考!数据结构+软件工程+操作系统
微软计划在C# for VS Code扩展中加入闭源组件惹开发者唾弃