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

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念