首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

[linux][memory] 内存回收

前言: 前文《内存映射技术分析》描述了虚拟内存的管理、内存映射;《物理内存管理》介绍了物理内存管理。 本篇介绍一下内存回收。内存回收应该是整个Linux的内存管理上最难理解的部分了。 分析: 1,PFRA Page Frame Reclaim Algorithm,Linux的内存回收算法。 不过,PFRA和常规的算法不同。比如说冒泡排序或者快速排序具有固定的时间复杂度和空间复杂度,代码怎么写都差不多。而PFRA则不然,它不是一个具体的算法,而是一个策略---什么样的情况下需要做内存回收,什么样的page适合做回收,回收多少算OK,实在回收不了怎么办呢。。。所以,PFRA整体上是策略,还有很多经验值。 2,watermark 如前文《物理内存管理》提到,每个memory zone都有watermark,如果低于了watermark,Linux就考虑回收内存了。 在linux-4.0.4/mm/page_alloc.c中:

分配page之前,先判断zone的watermark是否是OK的(by zone_watermark_ok),如果watermark不满足,则先尝试回收(by zone_reclaim)。回收之后要是满足了,就可以尝试在这个zone中进行申请page了。 3, zone_reclaim 大部分实在都在linux-4.0.4/mm/vmscan.c实现,并会调用到shrink_zone:

大概逻辑就是: 遍历memory cgroup,通过lru回收(by shrink_lruvec),再回收slab(by shrink_slab)。一般带有shrink_xxx的函数,都和内存回收相关。这里说明一下,回收cgroup是在相对比较新的kernel上才有的feature。作者在3.10上没有看到这个feature。 4,slowpath 尝试回收zone,但是不代表一定就可以回收到满足条件的page。Linux还会进一步尝试,只不过这次会慢一些。

在slowpath中,会唤醒kswapd(by wake_all_kswapds),再尝试申请内存;如果从freelist中还是申请不到,就直接回收(by __alloc_pages_direct_reclaim)。 可见,这条路径确实比较慢,所以系统在内存比较紧张的时候,就会比较卡,kswapd也会相对活跃。 5,shrinker 在 shrink_slab中:

会调用所有的shrinker。slab当然也会注册shrinker。 不过,作者在Android还见过一种比较有趣的shrinker---lowmemorykiller。系统中内存实在回收不到的时候,最差情况就是OOM,kernel开始杀用户进程(选择杀哪个,是kernel根据很多参数计算出来的)。不过,在Android的上,应用是可以被杀掉的,但是不希望Launcher被杀掉,于是lowmemorykiller尽量保证不杀掉launcher,而优先杀掉其他应用。这样就可以避免下一步触发OOM而杀掉Launcher。 6,reverse mapping 上面的策略,是决定什么时候回收,回收哪些page。 真正要回收的时候,要做什么呢?对于一个page,首先需要判断它是否可以被 回收,比如说kernel被load进的内存,是不能回收的,用户进程使用mlock来lock住的内存,也是不能回收的。 如果一个page可以被回收,那么这个page有可能被一个进程使用,还有可能被多个进程共享使用,还有可能被kernel使用。想要回收page的话,就要知道page被映射的所有的VMA(见前文《内存映射技术分析》),修改PTE,president位置0(这样下次再访问到这个page的时候,MMU就会产生page fault),释放page。 从虚拟内存到物理内存的过程,叫映射(mapping);从物理内存到虚拟内存的过程,叫反向映射(reverse mapping)。 关于reverse mapping,在linux-4.0.4/mm/rmap.c中主要实现。下面在来分析两种类型的page的回收---file mapping & anonymous mapping。 7,file mapping 映射了文件的VMA,就是file mapping。一种是用户进程调用了mmap映射了具体的文件,还有一种就是kernel为打开的文件创建的。 File mapping因为有对应的文件,那么回收起来其实是简单的:对于没有写过的page(不是脏页),只需要做反向映射,直接回收page就行了;对于dirty page,那么需要回写,再反向映射,最后回收。 说起来比较简单,然而实现上却很复杂,file mapping的一个address space是通过radix tree组织起来的。这就是传说中的page cache。主要实现逻辑在linux-4.0.4/mm/filemap.c中。 8, anonymous mapping 通过mmap(MAP_ANONYMOUS),brk,shmat,申请到的没有具体文件映射的内存,就是匿名映射,对应的page叫做匿名页。匿名页如果想要回收,就要找地方把page的内容写下去,等到需要的时候,再把内容load进内存---因为没有对应的具体文件,所以就要使用swap了。 这里还有一个关键技术:AVC( anonymous vma chain)。Linux通过avc遍历所有的映射了当前page的VMA,做反向映射。 9,kswapd 内核线程,负责内存回收。zone的watermark不满足的时候,就需要唤醒kswapd来回收内存。 10,lru list 内存回收lru选择那些内存需要回收。 敲命令cat /proc/meminfo:

查看当前系统的内存信息,注意有active和inactive之分,anon和file之分。如果file量很大,可能系统中映射了比较大的文件或者比较多的文件;如果anon比较多,再通过top、procrank、slabtop分析哪里使用的多。 11,OOM Linux默认对用户程序采取最大化信任的。比如说,分配内存的时候,只要不超过cgroup或者limit限制,linux都会分配的。例如在1G的机器上malloc了1.5G。只是说,如果内存不够了,linux会尝试回收,尽量满足。 回收的时候,如果实在回收不到了,linux会选择最差的策略---杀掉一部分进程。 OOM是out of memory的缩写,翻译成中文,作者觉得是不是“壮士断腕”呢?Orz 这里还有一个参数需要注意:cat /proc/sys/vm/panic_on_oom 如果是0,那么发生oom的时候,kernel直接杀掉进程而已。 如果是2,那么会判断NUMA的一些条件,决定是否panic。 如果是其他值,比如说1,那么就直接panic了。 关于这个值,作者和另外一个人讨论过,觉得这个值要慎重使用。发生了OOM,确实是内存紧张,不过这种情况依然在kernel的管理范围内,kernel能够hold住这种情况。对于kernel能够hold住的情况,最好不要让kernel直接panic。机器上还运行着其他的进程呢~尤其对于web服务器这种,像CGI/Fastcgi这种,动辄就运行几百甚至上千个,随便杀几个关系也不大,但是让整台机器挂掉,那么多请求都没法处理了。 后记: 虚拟内存的管理,内存映射,物理内存管理,内存回收,差不多就是Linux内存管理的主要功能了。 Good Luck~

下一篇
举报
领券