linux memory overcommit机制--------笔记

2024-05-03 22:08

本文主要是介绍linux memory overcommit机制--------笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:

overcommit 机制介绍:

一个问题引发的对overcommit的思考:

问题背景:

问题:

问题分析:

问题的原因:

解决方案:


前言:

linux的虚拟内存支持overcommit(过度使用)

本文就fork子进程时"fork: Cannot allocate memory" 错误展开分析,解释其原因,并给出解决方法.

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)

 

一个问题引发的对overcommit的思考:

问题背景:

我们在写程序的时候可能会涉及到多进程,譬如主进程fork一个子进程,或者更一般的我们会用到system和popen这些系统调用来执行我们的command。实际上system和popen也是通过fork子进程来完成自己的工作的。

system会先fork一个子进程,然后watpid(这里fork动作和waitpid动作都是在主进程中执行的)。子进程通过exec替换其上下文空间为shell进程,shell进程再fork一个子进程,然后waitpid。这个子进程同样的通过exec替换自己的上下文空间为要执行的command,执行command。popen的动作跟system一致,不同的是主子进程间有管道来进行通讯,譬如我可以获得command执行的输出结果。

问题:

由于使用system我们可能会遇到这样一个令人匪夷所思的问题:

fork: alloc memory fail

问题分析:

为了搞清楚这一个问题的原因,我举一个例子,通过这个例子来解释清楚为什么会出现这一问题。

例子:

系统当前有50M可用的物理内存(可用的物理内存同上一节的解释),启动一个进程:

  1. 分配1M大小的全局数组(这1M出现在了heap段)
  2. 申请2段15M大小的内存(malloc大于128k时采用mmap分配,出现在mmap段)
  3. 向这30M内存执行写操作(不执行这一步可fork成功)
  4. 执行fork
  5. 子进程sleep 10s,退出
  6. 父进程收到子进程退出消息后, sleep 10s
  7. 父进程释放内存. 退出

对于这个进程的内存空间的分布可以查看进程的maps(/proc/pid/maps)

我分配了15*2+1=31M的内存,但是应该还有19M内存可用,为什么fork会提示alloc memory fail呢????

                                                                         meminfo

 

                                                                            maps

 

                                                                            overcommit

为了弄明白原由,我们需要先知道在fork的时候子进程会copy父进程的一些虚拟内存区域,通过我们的实验分析可以知道它需要copy数据段,head段,部分mmap段(譬如这里我们malloc的30M内存)。在copy每一个内存区域的时候都会按照当前的overcommit策略来断定是否允许执行copy。overcommit的实现关键逻辑在"__vm_enough_memory(...)"函数。我们上面已经介绍了overcommit的三种策略,默认情况下采用启发式overcommit策略,这里不再阐述了。

在执行fork时,从系统调用开始_do_fork--->copy_process--->copy_mm--->dup_mm--->dup_mmap--->对父进程mm_struct中的每个vm_area执行security_vm_enough_memory_mm--->__vm_enough_memory,根据overcommit策略判断是否允许申请,如果允许事情则接着执行copy_page_range对页目录和页表进行拷贝

(同样的malloc一段内存的时候也会执行overcommit检查)

对于上面的例子:

(1) 通过在执行fork前,cat得到meminfo信息,计算得到当前可用物理内存 7500+2648+8816-144+1624-1068=19376Kbytes
这一结果与overcommit图中free的4861pages(19444Kbytes)基本接近(运行过程中内存存在变动,还有为数不多的其他几个进程在运行)。

(2) maps图中,第二行是data段(未初始化时大小为1个page),第三行是heap段(1M大小的全局数组分布在这个段内),第四行是一段mmap段(malloc超过128Kbytes时通过mmap分配,大小是30M+2pages,分量次申请的数据存在同一mmap段;当然还有其他mmap段,譬如文件的映射段,贡献库的映射段,这些都不需要拷贝),这三段是在fork时需要拷贝的vm_area,fork时拷贝这三段也是处于cow的考虑吧(写时拷贝)

(3)overcommit图中(我在驱动中加的log),第一行是data段(可以通过size和start_address与maps图所展示的信息断定,下同),第二行是1M的heap段,第三行是32M+2pages的一段mmap段

问题的原因:

通过overcommit图可以得知在fork时依据overcommit的决段逻辑(默认的启发式),30M+2pages的mmap段的大小(7682pages)超过了当前可用的物理内存大小19444Kbytes(4861pages).导致"alloc memory fail"

这样对"malloc的30M内存不执行写操作,fork成功"的原因就显而易见了,因为它没有去分配页框,物理内存不会因此减小30M+2pages,所以overcommit检查的时候能够通过,fork得以成功执行.(需要拷贝的最大的vm_area 30M+2pages小于当前可用的物理内存19444Kbytes+30M+ 2pages)。

解决方案:

可以使用vfork替代fork,vfork共享父进程的内存空间,不会进行copy,对资源的消耗更少,所以不会出现上述问题。而且vfork的kernel机制保证了vfork后子进程先运行(fork之后父子进程谁先运行时不确定的,当然你可以通过/proc/sys/kernel/sched_child_runs_first来使子进程先运行)。

要注意在vfork后子进程共享父进程的内存空间,不要因为子进程的一些动作而影响父进程的运行。其一般的用途是vfork之后即调用exec来替换子进程的上下文,从而执行我们的另外一个程序,避免了fork对父进程的没必要的拷贝。

这篇关于linux memory overcommit机制--------笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/957762

相关文章

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

Linux系统中卸载与安装JDK的详细教程

《Linux系统中卸载与安装JDK的详细教程》本文详细介绍了如何在Linux系统中通过Xshell和Xftp工具连接与传输文件,然后进行JDK的安装与卸载,安装步骤包括连接Linux、传输JDK安装包... 目录1、卸载1.1 linux删除自带的JDK1.2 Linux上卸载自己安装的JDK2、安装2.1

Linux卸载自带jdk并安装新jdk版本的图文教程

《Linux卸载自带jdk并安装新jdk版本的图文教程》在Linux系统中,有时需要卸载预装的OpenJDK并安装特定版本的JDK,例如JDK1.8,所以本文给大家详细介绍了Linux卸载自带jdk并... 目录Ⅰ、卸载自带jdkⅡ、安装新版jdkⅠ、卸载自带jdk1、输入命令查看旧jdkrpm -qa

Linux samba共享慢的原因及解决方案

《Linuxsamba共享慢的原因及解决方案》:本文主要介绍Linuxsamba共享慢的原因及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux samba共享慢原因及解决问题表现原因解决办法总结Linandroidux samba共享慢原因及解决

java中反射(Reflection)机制举例详解

《java中反射(Reflection)机制举例详解》Java中的反射机制是指Java程序在运行期间可以获取到一个对象的全部信息,:本文主要介绍java中反射(Reflection)机制的相关资料... 目录一、什么是反射?二、反射的用途三、获取Class对象四、Class类型的对象使用场景1五、Class

新特性抢先看! Ubuntu 25.04 Beta 发布:Linux 6.14 内核

《新特性抢先看!Ubuntu25.04Beta发布:Linux6.14内核》Canonical公司近日发布了Ubuntu25.04Beta版,这一版本被赋予了一个活泼的代号——“Plu... Canonical 昨日(3 月 27 日)放出了 Beta 版 Ubuntu 25.04 系统镜像,代号“Pluc

Linux安装MySQL的教程

《Linux安装MySQL的教程》:本文主要介绍Linux安装MySQL的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux安装mysql1.Mysql官网2.我的存放路径3.解压mysql文件到当前目录4.重命名一下5.创建mysql用户组和用户并修

Linux上设置Ollama服务配置(常用环境变量)

《Linux上设置Ollama服务配置(常用环境变量)》本文主要介绍了Linux上设置Ollama服务配置(常用环境变量),Ollama提供了多种环境变量供配置,如调试模式、模型目录等,下面就来介绍一... 目录在 linux 上设置环境变量配置 OllamPOgxSRJfa手动安装安装特定版本查看日志在

Linux系统之主机网络配置方式

《Linux系统之主机网络配置方式》:本文主要介绍Linux系统之主机网络配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、查看主机的网络参数1、查看主机名2、查看IP地址3、查看网关4、查看DNS二、配置网卡1、修改网卡配置文件2、nmcli工具【通用