使用systemtab hook fork,定位到报错调用路径SYSCALL_DEFINE0(fork)-》kernel_clone-》copy_process-》copy_mm-》dup_mm-》dup_mmap-》security_vm_enough_memory_mm-》__vm_enough_memory
__vm_enough_memory返回了 -ENOMEM。其源码如下:
从代码可知fork需要拷贝父进程的虚拟空间给子进程,父进程是一个使用了巨大虚拟内存的进程。overcommit机制策略影响虚拟内存分配,默认策略是0,即剩余系统物理内存+swap<当前要分配的虚拟内存,就返回失败。
解决方案,将overcommit策略设置为1,即overcommit_always.
overcommit 机制介绍:
linux memory overcommit策略一共分为三种(可以通过/proc/sys/vm/overcommit_memory修改策略):
参考linux kernel document:overcommit-accounting
参考内核mm/util.c __vm_enough_memory()
(1)启发式策略(overcommit_memory==0):
如果有足够可用的物理内存供使用,则内存分配成功,否则失败.
可用物理内存可以通过对/proc/meminfo的统计信息做一下计算得到:"free + buffer + cached - shm + swap + slab_recalimable - zone_total_reserved(各zone预留内存总和,一般是固定的)"
__vm_enough_memory通过比较请求的内存数量与当前可用的物理内存来决定是否允许请求。
(2)允许过度使用策略(overcommit_memory==1):
无论分配多少内存都会成功,这样的好处是可以使用所有的物理内存,但是有可能引发oom。(为了验证你可以动手试一下,譬如当前有50M可用的物理内存,malloc即使100M也是成功返回的,但是写这段内存的时候,超过50M就是oom)
(3)不允许过度使用策略(overcommit_memory==2):
系统的所有虚拟内存加起来不得超过 “总物理内存 + CommitLimt”(CommitLimt=总物理内存*overcommit_ratio%)。overcommit_ratio可以在/proc/sys/vm里设定,默认是50,也就是CommitLimt默认是0.5倍的总物理内存。当前所有进程一共使用的虚拟内存Committed_AS和CommitLimt可通过/proc/meminfo查看。(为了验证你也可以试一下,看一下当你的Committed_AS超过“总物理内存 + CommitLimt”时,这时候你在终端上敲个命令就会提示你fork:alloc memory fail)