Android的lowmemorykiller演变分析

        在学习Android的lowmemorykiller机制过程中,发现从KK到L再到M有一些新的变化,因此有必要进行一下总结。在开始分析前先厘清一些基础概念,便于描述的展开和结论的形成。本文内容并没涉及太多具体实现细节,目的只是为实际的开发提供纲领性指导意见。

一、基本概念

1、PageCache:Linux内核为加快文件预取而采用的特有机制(参考引用一),就是尽可能的把空闲内存用于缓存最近访问过的磁盘文件数据。由于会占用大量的空闲内存,在某种情况下就会导致OOM的发生。Linux内核的内存管理是很重要的部分,需要花很多时间和精力来学习(参考引用二)。

2、OOM:即“Out of Memory”的缩写,这个其实是标准Linux内核的一种内存管理机制,在内存不足或无法分配(大)内存的时候会触发此机制,来完成内存的回收。具体到代码级别,内存页面回收机制是Cache Shrinker,通过内核线程kswapd监控内存页面情况和触发Shrinker回调函数进行内存页面回收。

3、lowmemorykiller(简称LMK,下同):是Android基于标准Linux内核OOM机制修改而来,最初的方式是通过在内核实现一个staging形式的驱动,在此驱动中注册Shrinker回调函数和实现应用策略,剩下的工作就是定时检查及判断是否需要回收(或定时检测和内核触发相结合???)。关于OOM和LMK详细信息可参考引用三。

4、CGroup:简单的说就是对进程线程使用各种系统资源的分组管理和控制,包括如CPU、IO、MEMORY,等等。


20160308补充:

关于Linux的OOM和Android的LMK的差异对比在引用八有详细介绍。

二、LMK实现演变

1、在Android的KK(或之前)版本中,LMK机制和策略都是在内核空间驱动中实现的,驱动提供两个sys文件结点给AMS用于设置触发内存回收的阀值。

2、在Android的L(或之后)版本中,引入了一个处于用户空间的守护进程lmkd(参考引用四),LMK机制和策略的实现多了一种选择---即可在用户空间完成。但不管是旧的还是新的方式,原来由AMS完成的设置工作都通过socket方式传递参数给lmkd来实际完成。这样一来AMS与内核的耦合性进一步降低,把精力关注于activity的管理。

三、lmkd进程分析

关于lmkd的作用在修改日志中很明确:
Author: Todd Poynor 
Date:   Tue Jul 9 19:35:14 2013 -0700

    Add lmkd low memory killer daemon
    
    Move kernel low memory killer logic to new daemon lmkd.  ActivityManager
    communicates with this daemon over a named socket.

        为什么导入lmkd,根据我的经验分析一来在用户空间实现策略更容易优化和管控,二来LMK驱动是Android特有的东西不太容易进入Linux内核的正式驱动列表(一直是staging形式)。在google groups的一个讨论组(android-platform)的贴子中也提到了类似的观点(引用五,需自备楼梯):
Dual supported in L. For example N9 released with lmk due to its advanced architecture
giving an edge to lmk over lmkd. Low memory killing is an important feature on all 
Android Devices and its tuning and optimization is a requirement to provide the best 
and balanced user experience. The option of either improves the tuning options of the 
device manufacturers. Support for lmk scales because of its inclusion upstream in the 
kernel. Our goal is to improve lmkd as an optimized replacement with algorithms that 
live well in the android ecosystem.

考虑到使用Android版本内核的差异性,lmkd进程的实现是兼容了新旧两种不同方式的:
(1) 使用LMK驱动(旧的方式),配置阀值参数保存在驱动中,LMK策略由驱动实现,内核通过Shrinker机制触发策略执行;
(2) 不使用LMK驱动(新的方式),配置阀值参数保存在lmkd进程中,LMK策略由lmkd进程实现。但为了触发策略的执行,必须定制内核加入memory cgroup,通过监控memory pressure event来触发LMK策略执行;

根据lmkd的实现意图,理论上来说应该使用第二种方式才对;

20160308补充:
        在引用七中,对于引入LMK守护进程的意图及其相比于LMK驱动的优势(比如优雅地结束进程和回收资源,更快的响应和处理low memory状况避免linux的oom触发,正确判断cache和计算free memory)有详细的介绍。有意思的是,这个邮件是由memory pressure event的提交人Anton Vorontsov发出的,让我不禁觉得他就是lmkd的发起者。


四、memory pressure event

        这个其实也是标准Linux内核在2013年4月就已经实现的功能(提交者为Anton Vorontsov,patch的标题是“memcg: Add memory.pressure_level events”)。基于memory cgroup,内核可以分析当前内存的使用情况压力等级(low、medium、critical),并把相关信息通过eventfd方式通知给用户空间的监听者执行相关动作(如内存回收)。
       
        memory pressure event的实现(引用六)经过四次review(多人参与讨论,包括Linus Torvalds、Andrew Morton等大boss)才进入mainline,不得不配服国外开源环境的活跃及严谨治学态度。在这一系列讨论中也有人提及Android相关考虑,作者的想法就是实现一个通用的方式(包括对Android等移动设备支持)。

        从lmkd是2013年7月提交时间来看,借助memory pressure event方式的实现,google终于如愿以标准Linux内核方式消除了staging形式LMK驱动这个“鱼刺”。至于使用memory pressure event方式的内存回收效率及实际效果如何,未进行相关测试前就不得而知了。

五、引用

1、linux的page cache策略
http://blog.csdn.net/fall221/article/details/46290563

2、linux内存管理各文件简介
http://blog.csdn.net/u011955950/article/details/18860379

3、android Low Memory Killer介绍
http://blog.csdn.net/hgl868/article/details/6744883

4、Android——内存管理-lowmemorykiller 机制
http://blog.csdn.net/jscese/article/details/47317765

5、why lmkd and logd are created to move log and lmk from kernel to userspace in L?
https://groups.google.com/forum/#!topic/android-platform/PW85I7qRBP8

6、memcg: Add memory.pressure_level events
https://patchwork.kernel.org/patch/2123031/
https://patchwork.kernel.org/patch/2161601/
https://patchwork.kernel.org/patch/2318281/
https://patchwork.kernel.org/patch/2384491/

7、Userspace low memory killer daemon
https://lwn.net/Articles/511731/

8、What is the difference between low memory killer and out of memory killer?
https://www.quora.com/What-is-the-difference-between-low-memory-killer-and-out-of-memory-killer

你可能感兴趣的:(linux)