LMK低内存管理机制

Android的Low Memory Killer(LMK)是基于Linux的OOM改进的内存管理机制,它定时检查并根据oom_adj值判断进程重要性,当内存不足时杀死低优先级进程。LMK通过adj阈值表配置不同内存级别的阈值,动态调整内存回收策略。其核心是lowmem_shrink回调函数,根据当前内存状态选择并杀死相应进程。
摘要由CSDN通过智能技术生成

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)

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值