1 low memory killer
Android的lowmemory killer是基于linux的OOM(out ofmemory)规则改进而来的。OOM通过一些比较复杂的评分机制,对运行进程进行打分,然后将分数高的进程判定为bad进程,杀死进程并释放内存。OOM只有当系统内存不足的时候才会启动检查,而lowmemory killer则不仅是在应用程序分配内存发现内存不足时启动检查,它也会定时地进行检查。
Low memory killer主要是通过进程的oom_adj来判定进程的重要程度的。oom_adj的大小和进程的类型以及进程被调度的次序有关。
Low memory killer的具体实现在kernel,比如对于androidL,代码在:/local/sourcecode/AndroidLSoul45/kernel-3.10/drivers/staging/android/lowmemorykiller.c。由于是根据MTK释放的androidL源码来研究,里面难免有相当篇幅是MTK自己添加的,目的是定制mtk自己memroy优化方案。本文介绍的时候可能会忽略这一部分代码,回归android原生系统。
1.1 基本原理
Low Memory Killer与OOM的区别:OOM即Out ofMemory是标准linuxKernel的一种内存管理机制,LowMemory Killer在它基础上作了改进:OOM基于多个标准给每个进程打分,分最高的进程将被杀死;LowMemory Killer则用oom_adj和占用内存的大小来选择Bad进程,OOM在内存分配不足时调用,而LowMemory Killer每隔一段时间就会检查,一旦发现空闲内存低于某个阈值,则杀死Bad进程。
基本实现原理:在linux中,存在一个名为kswapd的内核线程,当linux回收存放分页的时候,kswapd线程将会遍历一张shrinker链表,并执行回调,或者某个app分配内存,发现可用内存不足时,则内核会阻塞请求分配内存的进程分配内存的过程,并在该进程中去执行lowmemorykiller来释放内存。(具体kswapd怎么工作,没有研究)。
结构体shrinker的定义在AndroidLSoul45/kernel-3.10/include/linux/shrinker.h,具体如下:
struct shrinker {
int (*shrink)(struct shrinker *, structshrink_control *sc);
int seeks; /* seeks to recreate an obj */
long batch; /* reclaim batch size, 0 = default */
/* These are for internal use */
struct list_head list;
atomic_long_t nr_in_batch; /* objspending delete */
};
#defineDEFAULT_SEEKS 2 /* A good number if you don't know better. */
extern voidregister_shrinker(struct shrinker *);
extern void unregister_shrinker(structshrinker *);
#endif
根据上面的代码逻辑可以看出,只要注册shrinker,就可以在内存分页回收时根据规则释放内存。下面具体看看lowmemorykiller.c里是怎么实现的。
1.2 lowmemorykiller实现机制
下面具体介绍lowmemorykiller的原理。
1.2.1初始化与注销
//module_init(lowmem_init);android4.4采用这种方式
late_initcall(lowmem_init);
module_exit(lowmem_exit);
模块加载和退出的函数,主要的功能就是register_shrinker和unregister_shrinker结构体lowmem_shrinker。主要是将函数lowmem_shrink注册到shrinker链表里。初始化模块时进行注册,模块退出时注销。
static structshrinker lowmem_shrinker = {
.shrink = lowmem_shrink,//函数指针
.seeks = DEFAULT_SEEKS * 16
};
//注册
static int __initlowmem_init(void)
{
#ifdef CONFIG_ZRAM
vm_swappiness = 100;
#endif
task_free_register(&task_nb);
register_shrinker(&lowmem_shrinker);
return 0;
}
//注销
static void __exitlowmem_exit(void)
{
unregister_shrinker(&lowmem_shrinker);
task_free_unregister(&task_nb);
}
当然这里进行初始化和注销工作的前提是先定义shrinker结构体,lowmem_shrink为回调函数的指针,当有内存分页回收的时候,获其他有需要的时机,这个函数将会被调用。
1.2.2阀值表
Android中,存在着一张内存阈值表,这张阈值表是可以在init.rc中进行配置的,合理配置这张表,对于小内存设备有非常重要的作用。我们来看lowmemorykiller.c中这张默认的阈值表:
static intlowmem_adj[6] = {
0,
1,
6,
12,
};
static intlowmem_adj_size = 4;
static intlowmem_minfree[6] = {
3 * 512, /*6MB */
2 * 1024, /*8MB */
4 * 1024, /*16MB */
16 * 1024, /* 64MB */
};
lowmem_adj[6]中各项数值代表阈值的警戒级数;lowmem_minfree[6]代表对应级数的剩余内存。也就是说,当系统的可用内存小于6MB时,警戒级数为0;当系统可用内存小于8M而大于6M时,警戒级数为1;当可用内存小于64M大于16MB时,警戒级数为12。Low memory killer的规则就是根据当前系统的可用内存多少来获取当前的警戒级数,如果进程的oom_adj大于警戒级数并且最大,进程将会被杀死(具有相同oom_adj的进程,则杀死占用内存较多的)。oom_adj越小,代表进程越重要。一些前台的进程,oom_adj会比较小,而后台的服务,oom_adj会比较大,所以当内存不足的时候,Low memory killer必然先杀掉的是后台服务而不是前台的进程。
1.2.3回调函数lowmem_shrink
staticint lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
{
struct task_struct *tsk;
struct task_struct *selected = NULL;
int rem = 0;
int tasksize;
int i;
int min_score_adj = OOM_SCORE_ADJ_MAX + 1;
int selected_tasksize = 0;
int selected_oom_score_adj;
int array_size = ARRAY_SIZE(lowmem_adj);
//这部分通过global_page_state()函数获取系统当前可用的内存大小,然后根据可用内存大小及内存阈值表,来计算系统当前的警戒等级。
int other_free =global_page_state(NR_FREE_PAGES) - totalreserve_pages;
int other_file =global_page_state(NR_FILE_PAGES) - global_page_state(NR_SHMEM);
if (lowmem_adj_size < array_size)
array_size = lowmem_adj_size;
if (lowmem_minfree_size < array_size)
array_size = lowmem_minfree_size;
for (i = 0; i < array_size; i++) {
if (other_free < lowmem_minfree[i]&&
other_file < lowmem_minfree[i]){
min_score_adj = lowmem_adj[i];
break;
}
}
if (sc->nr_to_scan > 0)
lowmem_print(3, "lowmem_shrink%lu, %x, ofree %d %d, ma %d\n",
sc->nr_to_scan, sc->gfp_mask,other_free,
other_file, min_score_adj);
//计算rem值
rem =global_page_state(NR_ACTIVE_ANON) +
global_page_state(NR_ACTIVE_FILE) +
global_page_state(NR_INACTIVE_ANON) +
global_page_state(NR_INACTIVE_FILE);
//如果内存状态还是不错,则返回rem,不做其他操作
if(sc->nr_to_scan <= 0 || min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
lowmem_print(5, "lowmem_shrink%lu, %x, return %d\n",
sc->nr_to_scan,sc->gfp_mask, rem);
returnrem;
}
//****下面开始遍历系统中所有的进程,选中将要杀掉的那个进程***
selected_oom_score_adj= min_score_adj;
rcu_read_lock();
for_each_process(tsk) {
struct task_struct *p;
int oom_score_adj;
//跳过内核线程,内核线程不参加这个杀进程
if (tsk->flags & PF_KTHREAD)
continue;
p = find_lock_task_mm(tsk);//有mm的thread才能操作
if (!p)
continue;
if (test_tsk_thread_flag(p, TIF_MEMDIE)&&
time_before_eq(jiffies,lowmem_deathpending_timeout)) {
task_unlock(p);
rcu_read_unlock();
return 0;
}
oom_score_adj =p->signal->oom_score_adj;// 得到task对应的oom_score_adj
//进程的oom_score_adj小于警戒阈值,则不处理
if (oom_score_adj < min_score_adj) {
task_unlock(p);
continue;
}
//获取进程所占用的内存大小,RSS值
tasksize =get_mm_rss(p->mm);
task_unlock(p);
if (tasksize <= 0)