本文转载自:https://blog.csdn.net/juS3Ve/article/details/80035753

摘要

slab、/proc/slabinfo和slabtop

用户空间malloc/free、内核空间kmalloc/kfee与Buddy的关系

mallopt

vmalloc

Linux为应用程序分配内存的lazy行为

内存耗尽(OOM)、oom_score和oom_adj

Android进程生命周期与OOM

1.    slab、/proc/slabinfo和slabtop

Buddy的最小单位是页(4k),无论是内核还是用户程序都会申请一些更小粒度的内存,所以在Linux内核中类似于堆的内存申请都会基于二次分配器slab。

slab原理:比如申请8 byte内存,就从Buddy申请到4K,然后将4K分成很多个8 byte,每一个8 byte就叫做Slab的一个object。

slab机制的实现算法有slab,slub,slob。

Linux会针对一些小粒度内存的申请以及一些常规数据结构的内存申请做slab,可以cat /proc/slabinfo查看。

可以看出slab主要分为两类:一类是内核里常用的数据结构,如TCPv6,UDPv6等,由于内核经常要申请和释放这类数据结构,所以就针对这些数据结构做一个slab,然后再次申请这类结构体时就总是从这个slab里来申请一个object(使用kmem_cache_alloc()申请)。另一类是一些小粒度的内存申请,如slabinfo中的kmalloc-16,kmalloc-32等(使用kmalloc()申请)。

注意:slab是只针对内核空间的,与用户空间没有关系。

slabtop

slabtop displays  detailed  kernel slab  cache  information in real time.  It displays a listing of the top cachessorted by one of the listed sort criteria. It also displays a statistics header filled with slab layer information.

2. 用户空间malloc/free、内核空间kmalloc/kfee与Buddy的关系

所有的内存申请最终都来自Buddy,但malloc/free及kmalloc/kfree都不与Buddy一一对应,libc和slab都相当于二级分配器。

slab与Buddy的关系:

  • slab与Buddy都是内存分配器。

  • slab的内存来自Buddy

  • slab与Buddy在算法上级别对等。Buddy把内存条当作一个池子来管理,slab是把从Buddy拿到的内存当作一个池子来管理的。

3.    mallopt

mallopt(M_TRIM_THRESHOLD, -1UL); 控制libc把内存还给内核的门限。把门限设置为-1UL表示在任何情况下,libc都不把内存还给内核。则从<do your RT-thing>之后,再malloc和free都是在之前申请的100MB内存池中进行,都不再与内核打交道,程序的实时性得以提高。这是一个在实时系统下的编程技巧。

4.    vmalloc

首先内存地址空间是包括物理内存+寄存器的,CPU在访问内存地址空间时都是经过virt->mmu->phys的过程。

图中内存空间中不同颜色的点都代表一页内存,无论是高端内存还是低端内存,都有可能被kmalloc,vmalloc和用户空间的malloc申请走(Buddy算法即是管理这一页是否被申请走的)。

malloc、vmalloc与kmalloc的唯一区别是malloc、vmalloc申请内存后需要修改页表,而kmalloc申请内存时由于已经做了开机线性映射,所以不需要修改页表。

寄存器是通过ioremap向vmalloc映射区去映射的,一旦调用ioremap,Linux就从vmalloc映射区找一个空闲的虚拟地址空间,然后去修改进程的页表,把这个虚拟地址往这个寄存器的物理地址去指。

vmalloc区域完成两个作用:

1)调用vmalloc从内核中申请内存并映射到vmalloc区

2)寄存器通过ioremap也映射到vmalloc区域

可以通过/proc/vmallocinfo文件查看

附注:

MMU通过虚拟地址察物理地址,不同的虚拟地址是可以对应一个相同的物理地址的:

理解页表最简单的方法(先只考虑一级页表),可以把页表想象成一个一维数组(a[1M],1M个成员是因为32位宽的地址,低12位作页内偏移,高20位对应第几页),CPU访问虚拟地址时,MMU用高20位作为数组的下标去取物理地址,比如假设成取a[i]成员,则a[i]中存物理地址,RWX权限和user/kernel权限。所以,两个不同的虚拟地址a[i]和a[j]中的内容(物理地址)相同是完全可能的。

5.    Linux内核为应用程序分配内存的lazy行为

如,在用户空间成功申请100M内存时并没有真的申请成功,只有100M内存中的任意一页被写的时候才真的成功。

用户空间malloc成功申请100M内存时,Linux内核将这100M内存中的每一个4K都以只读的形式映射到一个全部清零的页面(这其实不太符合堆的定义,堆一般是可读可写的),当任意一个4K被写的时候即会发生page fault,Linux内核收到缺页中断后就可以从硬件寄存器中读取到缺页中断的地址和发生原因。之后Linux内核根据缺页中断报告的虚拟地址和原因分析出是用户程序在写malloc的合法区域,此时Linux内核会从内存中新申请一页内存,执行copy on write,把全部清零的页面重新拷贝给新申请的页面,然后把进程的页表项的虚拟地址指向一个新的物理地址。同时,页表中这一页地址的权限也修改为R+W的。注意以页单位发生page fault。

VSS - Virtual Set Size

RSS - Resident Set Size

图中第一步,堆初始为8K已写8K,所以RSS为8K;第二步调用brk扩展堆为16K,此时VSS变为16K,但RSS仍然是8K;第三步,写堆的第三页发生page fault;第四步,写时拷贝,RSS变为12K。以此类推,写第四页成功后RSS才会变为16K。

注:Lazy机制可以理解为“欺骗应用程序”。但在内核空间调用kmalloc是不欺骗的,要么分配成功,要么分配失败。

6.    内存耗尽(OOM)、oom_score和oom_adj

Linux在运行时会对每一个进程进行一个Out of Memory的打分(基于进程所耗费内存的大小,耗费越多分数越高)。可以通过/proc/pid/oom_score文件查看分数。一旦内存耗尽,Linux内核会kill掉当前oom_score分值最高的进程。

例1:编译图上程序,swapoff -a将交换分区关闭,并且配置overcommit_memory为1(允许应用程序申请很大的内存,而内核不再去评估系统中当前还有多少内存可用。):sudo sh -c ‘echo 1 > /proc/sys/vm/overcommit_memory’(echo不会启动一个新的进程,所以加sh -c,在新的shell中执行,这样才能使sudo有效),然后运行。可以发现在运行一段时间后此程序被杀死,dmesg查看:

Out of memory:score分数848被杀死。

另外,OOM打分还会看一些其他的因子,如下图所示:

例2:启动另一个进程firefox,同样关闭swap分区并配置overcommit_memory为1,然后将firefox的oom_score调到最高,运行例1中的a.out观察哪个进程先被杀死。

如上图所示,写入到oom_adj的数值越大,导致oom_score的打分越高,越容易被杀死,此时写入不需要root权限,但想使其打分值变小则需要root权限,这也是符合现实意义的。

运行例1中的a.out,可以发现由于firefox的oom_score更高,所以先被杀死,但一段时间过后再次发生Out of memory,a.out也被杀死。

7.    Android进程生命周期与OOM

Android在程序退出时候,并不杀死进程,而是等OOM发生后再杀死。Android根据不同的进程类型设置不同的oom_adj。这样做的目的就是为了最大程度上的提高用户体验。

郝健: Linux内存管理学习笔记-第2节课【转】的更多相关文章

  1. 郝健: Linux内存管理学习笔记-第1节课【转】

    本文转载自:https://blog.csdn.net/juS3Ve/article/details/80035751 摘要 MMU与分页机制 内存区域(内存分ZONE) LinuxBuddy分配算法 ...

  2. Linux内存管理学习笔记 转

    https://yq.aliyun.com/articles/11192?spm=0.0.0.0.hq1MsD 随着要维护的服务器增多,遇到的各种稀奇古怪的问题也会增多,要想彻底解决这些“小”问题往往 ...

  3. Linux内存管理学习笔记——内存寻址

    最近开始想稍微深入一点地学习Linux内核,主要参考内容是<深入理解Linux内核>和<深入理解Linux内核架构>以及源码,经验有限,只能分析出有限的内容,看完这遍以后再更深 ...

  4. Linux内存管理学习笔记--物理内存分配

    http://blog.chinaunix.net/uid-20321537-id-3466022.html

  5. C++内存管理学习笔记(7)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  6. C++内存管理学习笔记(5)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  7. C++内存管理学习笔记(6)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  8. Linux内存管理学习资料

    下面是Linux内存管理学习的一些资料. 博客 mlock() and mlockall() system calls. All about Linux swap space 逆向映射的演进 Linu ...

  9. C++内存管理学习笔记(4)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

随机推荐

  1. appium 'WebDriver' object has no attribute 'keyevent'

    这个问题是我自己犯二了,开头应该是from appium import webdriver,写成了from selenium import webdriver,也可以运行,就是不能使用appium中独 ...

  2. windows核心编程 DLL技术 【转】

    注:本文章转载于网络,源地址为:http://blog.csdn.net/ithzhang/article/details/7051558 本篇文章将介绍DLL显式链接的过程和模块基地址重定位及模块绑 ...

  3. NormalMap 贴图 [转]

    转载: http://www.zwqxin.com/archives/shaderglsl/review-normal-map-bump-map.html   说起Normal Map(法线贴图),就 ...

  4. API测试工具postman使用总结

    一.Postman介绍: Postman是google开发的一款功能强大的网页调试与发送网页HTTP请求,并能运行测试用例的的Chrome插件,主要用于模拟网络请求包,快速创建请求,回放.管理请求,验 ...

  5. Oracle内存管理(之五)

    [深入解析--eygle]学习笔记 1.4. 2其它内存组件 Large Pool-大池是SGA的一个可选组件,通经常使用于共享server模式(MTS). 并行计算或 RMAN的备份恢复等操作. J ...

  6. JAVA Eclipse开发Android如何让超出界面的部分自动显示滚动条

    在原有布局的最外围添加一层ScrollView,注意原有布局的声明也要删了 <ScrollView xmlns:android="http://schemas.android.com/ ...

  7. 如何修改mindmanager默认字体为微软雅黑

    mindmanager默认的字体是Verdana的,如何改为大家喜欢的其他字体呢?比如微软雅黑. 其实很简单,以我使用的是汉化版Mindmanager2012为例,只需要下面几个步骤   第1步:找到 ...

  8. 加入 centos 右键 terminal

    centos6.2以上默认右键都没有terminal,现加入方法 例如以下 1>  yum -y install nautilus-open-terminal 2> shutdown -r ...

  9. mac环境下清理系统垃圾clearMyMac 3.9 破解版

    mac环境下清理系统垃圾clearMyMac 3 轻轻松松清理好几十G的垃圾文件 下载地址 链接: https://pan.baidu.com/s/1XZbZwzhgQCnzpvQDvyQrRA 密码 ...

  10. iOS打包(ipa包)

    1.打开XCode打开project文件.选择Product,再点击Archive. 2.鼠标右键点击Shoe In Finder 3.鼠标右键选择"显示包内容" 4.鼠标左键双击 ...