转自:http://blog.youkuaiyun.com/u012440406/article/details/51960387
参考:http://www.cnblogs.com/frydsh/p/3735953.html
最近在处理一些lowmemorykiller相关的问题,于是对lowmemorykiller机制作了一个简单的了解。在这里总结一下。
首先,是lowmemorykiller的一些背景知识。
众所周知,Andorid的实质是一个Linux的操作系统。所以和其他操作系统一样,每个程序,每个进程运行,都需要一定内存空间进行支撑。而进程的内存空间只是虚拟内存,程序运行需要的是实实在在的内存(物理内存,即RAM)。所以在必要的时候,操作系统会将程序运行中申请的内存映射到RAM中。RAM作为进程运作不可缺的资源,对系统稳定性有着决定性影响。所以我们必须对内存相关有一个简单直观的认知。进程空间1和RAM之间的关系大致如图:
内存相关的介绍,在这里我只是做一个简单介绍。假如想深入了解请自行了解操作系统相关知识。
简单对内存有一个了解之后,我们来简单介绍一下OOM。
OOM全称Out Of Memory,是linux当中,内存保护机制的一种。该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了防止内存耗尽而内核将该进程杀掉。
当Kernel遇到OOM的时候,可以有2种选择:
1) 产生kernelpanic(死机)
2) 启动OOM killer,选择一个或多个“合适”的进程,干掉那些选择中的进程,从而释放内存。
在OOM机制当中,有几个参数是必须了解的,这几个参数分别是oom_adj,oom_score_adj,oom_score。每个进程都会有这样的3个参数,他们位于/proc/XXX/目录下(XXX为进程的ID)。在Linux中,系统就是通过算分去杀死进程的。至于分数的值,就是这3个参数的值。
简单来说,系统是这样进行算分的:算分主要分2部分,一部分是系统打分,主要根据该进程的内存使用情况(oom_score),另一部分是用户大份额,就是oom_score_adj。每个进程的实际得分是综合这2个参数的值的。而oom_adj只是一个旧的接口参数,在普通的linux系统中和oom_score_adj是差不多的(但是在Android中是很有用的)。
OOM是android的LowMemoryKiller的基础。了解完OOM之后,我们终于可以引入LowMemoryKiller了。
在Android中,及时用户退出当前应用程序后,应用程序还是会存在于系统当中,这是为了方便程序的再次启动。但是这样的话,随着打开的程序的数量的增加,系统的内存就会不足,从而需要杀掉一些进程来释放内存空间。至于是否需要杀进程以及杀什么进程,这个就是由Android的内部机制LowMemoryKiller机制来进行的。
Andorid的Low Memory Killer是在标准的linux lernel的OOM基础上修改而来的一种内存管理机制。当系统内存不足时,杀死不必要的进程释放其内存。不必要的进程的选择根据有2个:oom_adj和占用的内存的大小。oom_adj代表进程的优先级,数值越高,优先级月低,越容易被杀死;对应每个oom_adj都可以有一个空闲进程的阀值。Android Kernel每隔一段时间会检测当前空闲内存是否低于某个阀值。假如是,则杀死oom_adj最大的不必要的进程,如果有多个,就根据oom_score_adj去杀死进程,,直到内存恢复低于阀值的状态。
LowMemoryKiller的值的设定,主要保存在2个文件之中,分别是/sys/module/lowmemorykiller/parameters/adj与/sys/module/lowmemorykiller/parameters/minfree。adj保存着当前系统杀进程的等级,minfree则是保存着对应的阀值。他们的对应关系如下:
举个例子说明一下上表,当当前系统内存少于55296×4K(即216MB)时,Android就会找出当前oom_adj≥9的进程,根据进程的等级,先把oom_adj数值最大的进程给杀掉,释放他的内存,当他们的oom_adj相等时,就对比他们的oom_score_adj,然后oom_score_adj越大,也越容易杀掉。
在这里,也许有人会问,为什么采用LowMemoryKiller而不用OOM呢?我们来对比一下两者,就可以得出答案了。
使用LowMemoryKiller可以使系统内存较低时,调出进程管理器结束不必要的人进程释放空间。在安卓中,如果等到真正的OOM时,也许进程管理器就已经没法启动了。
上面提到的oom_adj,其实在Android中并不只有6个,在Android 6.0中,一个设置了16个adj,adj的具体设置与描述如下:
对LowMemoryKiller相关的知识简介就到这里。下面我们就主要介绍一下lowMemorykiller的运行原理。
上面其实已经说过,LowMemoryKiller是对多个内存阀值的控制来选择杀进程的。但是,这些阀值是怎样联系在一起的呢?下面就我的理解,简单说一下其运行的原理。
首先,LowMemoryKiller是随着系统的启动而启动的。当前主要的LowMemoryKiller的代码主要在\system\core\lmkd的目录下,之前的代码\kernel\drivers\staging\android\lowmemorykiller.c已经不再使用。
LowMemoryKiller在系统启动的时候就已经由init进程一并启动了。LowMemoryKiller启动就是,就会不断监测系统的运行情况和内存情况,当内存少于minfree限定的阀值的时候,lowMemoryKiller遍历当前进程的oom_score_adj,把大于对应阀值的进程进行kill操作。例如,在当前设置中,当系统内存少于315M时,系统就会自动把进程中oom_score_adj的值少于1000的杀掉,当系统内存少于216时,系统就会自动把进程中oom_score_adj的值少于529的杀掉,如此类推。
至于oom_adj和oom_score_adj是由谁去控制并写入的呢?
在系统当中,oom_adj和oom_score_adj是由ActivityManagerService去控制的,上层应用的启动都离不开AcitivityManagerService的调用与分配资源。有关oom_adj与oom_score_adj会在以后分析ActivityManagerService的时候加入相对详细的论述。在这里就不详细说明。
有关Minfree的值的写入,其实可以找到很多个地方,但是在最开始(还在用lowmemorykiller.c)的时候,是可以在lowmemorykiller.c中设置的。但是现在已经不用lowmemorykiller.c了,所以相对设置的地方也不一样了。经查找验证,Minfree的阀值控制,是由ActivictyManagerService和lowmemorykiller一并控制写入的。
在ActivictyManagerService中,会调用ProcessList的applyDisplaySize()方法,从而调用updateOomLevels()的方法,开始算出相关的阀值.代码如下:
上面可以看出,LowMemoryKiller的阀值是通过多个判断然后算出来的。不过,在正常情况下,除了4,5等级之外,LowMemoryKiller的0-3的阀值其实就是mOomMinFreeHigh32Bit[]里面的值。我们以后假如需要对LowMemoryKiller的阀值进行定制,只需要针对这个方法去修改即可。
另外,刚刚说到,Minfree的阀值控制,是由ActivictyManagerService和LowMemoryKiller一并控制写入的。ActivictyManagerService把值确定了,通过socket机制,和LowMemoryKiller进行通讯,然后LowMemoryKiller就会把值写入minfree中。
在LowMemoryKiller的代码(lmkd.c)中,接受写入minfree的关键代码如下:
ctrl_connect_handler()在接受到数据之后,经过多步的处理最后会调用writefilestring()将值进行写入。
Minfree的值是每次开机都会进行写入的,所以假如我们只是单纯地在手机上,直接修改adj和minfree的值,重启之后是不会生效的。
那假如我们想定制某个应用的adj呢?假如只是定制单个应用的adj,其实我们可以在ActivityManagerService中的computeOomAdjLocked方法中进行定制。系统会调用这个方法不停更新正在运行的进程的adj。例如,假如我们想修改launcher默认的adj(默认launcher在后台运行时的adj为6),我们可以在computeOomAdjLocked中的:
进行修改即可。
我们可以通过app这个对象去获得更新的进程的包名以便定制。