android的oomkiller_Android Low Memory Killer 机制

Android的LowMemoryKiller在Linux的OOM Killer基础上工作,通过预设内存级别提前杀死进程,防止达到OOM状态。文章介绍了oom_score、oom_score_adj和oom_adj参数,以及Android如何根据app状态计算oom_adj值,并通过lowmem_minfree和lowmem_adj数组管理内存回收。LowMemoryKiller通过socket与lmkd通讯,调整进程优先级以优化内存使用。
摘要由CSDN通过智能技术生成

LowMemoryKiller是Android 系统在Linux kernel的OOMKiller基础上打的一个补丁。OOMKiller在kernel 没法再分配内存的时候,寻找一个得分最高的进程来杀掉。LowMemoryKiller则提前一步,通过把剩余内存划分成不同的级别,内存在消耗的过程中,触发不同的级别,杀死相应的app进程。在触发OOMKiller前,大量缓存的app进程已经被杀死掉了。

先简单说一下OOMKiller。

我们查看任一进程的proc信息(如:/proc/1), 都会看到以下三个参数:

oom_score -- 是该进程的最终得分,分数越高,越容易被杀死;

oom_score_adj -- 范围:[-1000, 1000],是kernel用来配置进程优先级的。值越低,最终的oom_score越低。

oom_adj -- 范围:[-16, 15],是给用户来配置进程优先级的。为了方便用户配置,提供了范围更小的oom_adj参数,数字越小优先级越高,-17表示该进程被保护,不被OOMKiller杀死。

因此,用户设置oom_adj后,kernel会转换并更新该进程实际的oom_score_adj值,它们的换算关系是:

#define OOM_DISABLE (-17)

#define OOM_SCORE_ADJ_MAX 1000

oom_score_adj = (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;

oom_adj = (oom_score_adj * - OOM_DISABLE) / OOM_SCORE_ADJ_MAX;

从上面可以看到,这个算法是有损的,所以,有时候会发现读出的oom_adj的值同设置得不一样了。如,设置oomajd=13, 那么 oom_score_adj=764,再次读oom_adj, 764 * 17 / 1000 == 12了。

当内存耗尽的时候,OOMKiller会调用 out_of_memory()来select_bad_process(), oom_score最大的值就是那个将要被杀死的bad process。 oom_badness()以oom_score_adj作为基础值,根据是否为管理员进程,占用的内存情况,来计算出最终的oom_score值,分值越高,越容易被杀死。

OOMKiller相应的源码文件:

/fs/proc/base.c

/mm/oom_kill.c

在Android系统里面,当app的状态发生变化,如:创建,收到广播,唤醒,放入后台等, ActivityManagerService的updateOomAdjLocked() 会computeOomAdjLocked(),然后,通过applyOomAdjLocked()把app的oom_adj值写入到Linux Kernel。

那么AN是如何计算每个app最终的oom_adj值的呢?

我们知道oom_adj的范围是[-16, 15],AN根据app进程的特性进行了分类,不同的类别,对应不同的数值。

9 ~ 15: 是缓存到后台的app。

8: service B 列表, 长时间未使用的service进程。

7: 前一个app。

6: Home app。

5: 包含service的app 进程。

4: 高权重的应用--隐藏的属性,AN P以上版本才能配置: android:cantSaveState="true"

3: backup app--正在被备份的app。

2: 用户可感知的后台进程,如:后台背景音乐播放器。

1: 前台app启动的一些可见的组件,如: 弹出的Email activity。

0: 前台app.

-11: persistent service -- android:persistent:=true

-12: 常驻内存的系统app--如:系统自带的拨号app。

-16: 系统进程--如:system_server进程。

-17: 不受oom_adj的管理,该进程不会被杀死,也native process的默认值。

LowMemoryKiller注册了shrinker--Linux Kernel的一个内存管理工具,当kernel需要回收内存时,会回调LowMemoryKiller的lowmem_shrink(),它先检查kernel 剩下多少内存,根据剩下的内存数量来匹配数组 lowmem_minfree[], 找到数组索引值,然后,再使用该索引值,从 lowmem_adj[]这个数组里面就得到目标oom_adj值,最终,在大于等于该目标oom_adj的进程中,杀死拥有最大oom_adj值的进程--send_sig(SIGKILL, selected, 0) 。算法其实很简单,就是两个一维数组的映射。

static short lowmem_adj[6] = {

0,

1,

6,

12,

};

static int lowmem_minfree[6] = {

3 * 512, /* 6MB */

2 * 1024, /* 8MB */

4 * 1024, /* 16MB */

16 * 1024, /* 64MB */

};

如: 系统剩下的内存为 31024, 它 小于 4 1024,对应的数组索引是 2, lowmem_adj[2]对应的是 6,那么系统将在oom_adj>=6的进程中,找一个最大的oom_adj的进程,然后,杀死它释放内存。

Android在初始化的时候,会通过ProcessList::updateOomLevels()来设定上面两个数组的初始值,我们可以通过framework的config.xml,或 /sys文件系统接口进行调整lowmem_minfree []。

/sys/module/lowmemorykiller/parameters/minfree

-1

0

config_....KbytesAbsolute:非-1的情况下,是绝对值, AN使用下面算法,得到实际数组值。

for ( int i = 0 ; i < mOomAdj.length; i++) {

mOomMinFree[ i ] = (int) ((float)minfree_abs * mOomMinFree[ i ] / mOomMinFree[ mOomAdj.length - 1 ] );

}

config_....KbytesAdjust: 非0情况下, 直接在每个数组值上 += reserve_adj;

如: -512,表明每个数组值都减少512。

lowmem_adj[] 可以通过/sys 文件系统接口来进行调整。

/sys/module/lowmemorykiller/parameters/adj

当然了在实际上开发过程中,也可以直接在这个函数里面打补丁,或者读取系统属性,通过属性来进行配置等等。 像MStar方案,就定义了两个属性来进行第三方的配置: ro.mstar.lmkd.minfree和ro.mstar.lmkd.adj

到这里,基本上LowMemoryKiller算是说完了,最后,简单介绍下,AN是如何把oom_adj值传给kernel的。

AN通过ProcessList:setOomAdj(),用socket与lmkd通讯, lmkd通过/sys文件系统接口,把oom_adj值传递给LinuxKernel。

/sys/module/lowmemorykiller/parameters/minfree

/sys/module/lowmemorykiller/parameters/adj

相关的源码文件分别在:

system/core/lmkd/ 生成 /system/bin/lmkd

drivers/staging/android/lowmemorykiller.c

framework/base/...../server/am/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值