前言

基于andorid4.4源码阅读和解说

关于LMK 网上已经有很多介绍,很多地方我就不做重复介绍, 这篇文章主要是介绍个人对LMK的理解和一些之前自己的疑惑

1 LMK是什么?
2 adj 是什么?
3 curRawAdj,setRawAdj,curAdj,setAdj是什么关系?
4 如何Killer进程的?
5 什么时后会killer?

初识LMK

最初学android 有一段时间的时后,为了写好一个应用,总是事先在网上搜索一下看有什么新东西,避免与世界脱轨,在阅读海量的信息中,经常看到, android 进程 分前台,可见,服务, 后台,空闲。在就是activity 当处于后台时,容易被系统杀死。特别是应用service,动不动就被系统杀死了。 一些防service死掉的方案百发齐放, 有提高service优先级,设置服务为前台,多个app互相监听和拉起服务。这些都是app应用层的手段。最终还是有概率会被系统杀死。 到底是什么一个东西像无情的杀手一样,重复并且不厌其烦的一遍又一遍的杀死我的应用,当时由于见识有限,或者android 那时针对这方面讲解太少,搜索不到关键点,也很少会去翻看源码,很长一段时间让自己有种挫败感,随着时间流逝,这个心中的疑问也埋藏起来。

甘于平凡,死于冷漠,不屈灵魂,遇火重生

后来慢慢的接触andorid的时间越来越长,对它的内部运行机制充满了好奇,在空余时间也会针对工作上的问题查看源码,虽然看起来很困难,结合网上的一些博客讲解,慢慢的能理解其中的一些设计意图和工作流程。

从而再次遇到这个无情的杀手,这次一定会揭开它神秘的面纱

它就是LMK,全称 Low Memory Killer ,字面意思理解,低内存杀手。 (还真是一个杀手(─‿‿─))
回到我的第一个问题

LMK是什么?

我们可以比喻android 系统是一个朝廷, 在朝廷需要银两打仗或者赈灾时,会评估当前的朝廷的剩余银两在哪一个等级; oom_score_adj 这个可以理解官员的品级, 数字越大的代表品级越低,杀起来也越没有什么心里负担。数字约小的代表品级约高,如果随意杀可能会引起朝廷动荡,所以需要平衡利弊才考虑是否值的杀。 当朝廷户部在白银1024两 至4096两之间的会把所有八品和八品以下的官员都列出来选一个品级最小贪污最多的抄家。 当朝廷户部在白银1024两一下,这时会把所有人都包含进去,当然不包括皇帝自己(内核线程),最后选一个品级最小贪污最多的抄家。

LMK的杀进程流程

lowmemorykiller,这里直接引用内核文件里的注释

/* drivers/misc/lowmemorykiller.c** The lowmemorykiller driver lets user-space specify a set of memory thresholds* where processes with a range of oom_score_adj values will get killed. Specify* the minimum oom_score_adj values in* /sys/module/lowmemorykiller/parameters/adj and the number of free pages in* /sys/module/lowmemorykiller/parameters/minfree. Both files take a comma* separated list of numbers in ascending order.** For example, write "0,8" to /sys/module/lowmemorykiller/parameters/adj and* "1024,4096" to /sys/module/lowmemorykiller/parameters/minfree to kill* processes with a oom_score_adj value of 8 or higher when the free memory* drops below 4096 pages and kill processes with a oom_score_adj value of 0 or* higher when the free memory drops below 1024 pages.** The driver considers memory used for caches to be free, but if a large* percentage of the cached memory is locked this can be very inaccurate* and processes may not get killed until the normal oom killer is triggered.** Copyright (C) 2007-2008 Google, Inc.** This software is licensed under the terms of the GNU General Public* License version 2, as published by the Free Software Foundation, and* may be copied, distributed, and modified under those terms.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the* GNU General Public License for more details.**/

大概的意思 如果把“0,8" 写入 /sys/module/lowmemorykiller/parameters/adj ,“1024,4096” 写入 /sys/module/lowmemorykiller/parameters/minfree,
含义如下:
当系统内存小于1024时则从adj 为0或者大于0的进程都有可能被杀死,在其中选择adj最大的并持有内存最多的进程将其杀死。
当系统内存小于4096大于1024 ,则从adj 为8或者大于8的进程都可能被杀死,在其中选择adj最大的并持有内存最多的进程将其杀死。

最终写入的数据组合会覆盖在下面的数组lowmem_adj 和lowmem_minfree

使用linux kernel的简易框架,具体可以网上查看 module_param_named
或者module_param_array_named 的相关说明

static short lowmem_adj[6] = {0,1,6,12,
};
static int lowmem_adj_size = 4;
static int lowmem_minfree[6] = {3 * 512,   /* 6MB */2 * 1024,  /* 8MB */4 * 1024,  /* 16MB */16 * 1024,    /* 64MB */
};
static int lowmem_minfree_size = 4;

在查看具体kill流程,在函数lowmem_shrink里

static int 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;//最低评分调整,默认按照最大的+1 等于 1001short min_score_adj = OOM_SCORE_ADJ_MAX + 1;//最低内存大小int minfree = 0;//选中进程的内存大小int selected_tasksize = 0;//选中进程评分调整short selected_oom_score_adj;//内存和评分区间的数组大小int array_size = ARRAY_SIZE(lowmem_adj);//系统剩余空间 ,这里解释的比较模糊,先这样理解,后续扩展这方面细节。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);/*reduce CMA pages from free pages when allocate non-movable memory.*/if (allocflags_to_migratetype(sc->gfp_mask) != MIGRATE_MOVABLE)other_free = other_free - global_page_state(NR_FREE_CMA_PAGES);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++) {minfree = lowmem_minfree[i];/*low down minfree to half when allocate non-movable memory.*/if (allocflags_to_migratetype(sc->gfp_mask) != MIGRATE_MOVABLE)minfree = minfree / 2;//当minfree小于系统内存则跳出循环,从而得到当前minfree  min_score_adj的值if (other_free < minfree && other_file < minfree) {//由于lowmem_minfree  lowmem_adj 数组是一一对应的,可以直接拿i获取对应的min_score_adjmin_score_adj = lowmem_adj[i];break;}}if (sc->nr_to_scan > 0)lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %hd\n",sc->nr_to_scan, sc->gfp_mask, other_free,other_file, min_score_adj);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);//当nr_to_scan小于等于0 或者 min_score_adj是默认值1001则直接return 此方法。 //nr_to_scan这个在源码中 kernel_imx/mm/vmscan.c  代表要在列表中浏览的页面数。具体不是很了解,后续扩展细节。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);return rem;}//选中的进程的评分调整, 第一次使用最低评分调整来初始化。selected_oom_score_adj = min_score_adj;// 内核一种同步机制 -- RCU同步机制  |不是很了解,后续扩展细节rcu_read_lock();//一个宏定义,用来遍历所有进程。for_each_process(tsk) {//进程结构体指针struct task_struct *p;//评分调整short oom_score_adj;//如果是内核线程则跳过if (tsk->flags & PF_KTHREAD)continue;//判断进程是否存在有效的内存p = find_lock_task_mm(tsk);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;}//评分调整等于p的评分调整oom_score_adj = p->signal->oom_score_adj;//如果当前进程的评分小于最低评分则跳过,进行下一进程遍历if (oom_score_adj < min_score_adj) {task_unlock(p);continue;}//获取进程的内存大小tasksize = get_mm_rss(p->mm);task_unlock(p);//如果进程的内存大小小于等于0则跳过进行下一个进程遍历if (tasksize <= 0)continue;//如果又选中过则与选中的进程比较if (selected) {//比较评分,如果当前小于选中的则跳过if (oom_score_adj < selected_oom_score_adj)continue;//不过评分想等,则在比较内存大小,当前内存小于选中的则跳过if (oom_score_adj == selected_oom_score_adj &&tasksize <= selected_tasksize)continue;}//赋值选中进程selected = p;//赋值选中的内存大小selected_tasksize = tasksize;//赋值选中的评分调整selected_oom_score_adj = oom_score_adj;lowmem_print(2, "select '%s' (%d), adj %hd, size %d, to kill\n",p->comm, p->pid, oom_score_adj, tasksize);}//遍历结束后如果欧选中的进程则开始杀死进程if (selected) {lowmem_print(1, "Killing '%s' (%d), adj %hd,\n" \"   to free %ldkB on behalf of '%s' (%d) because\n" \"   cache %ldkB is below limit %ldkB for oom_score_adj %hd\n" \"   Free memory is %ldkB above reserved\n",selected->comm, selected->pid,selected_oom_score_adj,selected_tasksize * (long)(PAGE_SIZE / 1024),current->comm, current->pid,other_file * (long)(PAGE_SIZE / 1024),minfree * (long)(PAGE_SIZE / 1024),min_score_adj,other_free * (long)(PAGE_SIZE / 1024));lowmem_deathpending_timeout = jiffies + HZ;send_sig(SIGKILL, selected, 0);set_tsk_thread_flag(selected, TIF_MEMDIE);rem -= selected_tasksize;}lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n",sc->nr_to_scan, sc->gfp_mask, rem);rcu_read_unlock();return rem;
}

这个函数主体步骤
1 通过lowmem_adj 和lowmem_minfree 和系统当前内存的状态获取最低评分标准
2 遍历基于最低评分标准大的进程找出评分最大且内存占用最多的进程。
3 找到进程后将其杀死。

adj计算与写入

这两个文件
/sys/module/lowmemorykiller/parameters/adj 作为优先级等级组
/sys/module/lowmemorykiller/parameters/minfree 作为对应的内存组
在AMS里这个类ProcessList.java 构造时写入的规则。
然后在AMS中通过applyOomAdjLocked这个方法来更新 进程的adj值。和这个方法相关的还有updateOomAdjLocked,computeOomAdjLocked。
大概的顺序是updateOomAdjLocked -> computeOomAdjLocked -> applyOomAdjLocked
个人理解触发这些方法的都是一些四大组件的生命周期变化,或者还有其他场景这里就不具体展开了。
主要了解 adj如何写入的。
在AMS内中的applyOomAdjLocked方法,会通过Process 调用setOomAdj来设置adj值。

private final boolean applyOomAdjLocked(ProcessRecord app, boolean wasKeeping,ProcessRecord TOP_APP, boolean doingAll, boolean reportingProcessState, long now) {//省略...if (app.curAdj != app.setAdj) {if (Process.setOomAdj(app.pid, app.curAdj)) {if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, "Set " + app.pid + " " + app.processName +" adj " + app.curAdj + ": " + app.adjType);app.setAdj = app.curAdj;} else {success = false;Slog.w(TAG, "Failed setting oom adj of " + app + " to " + app.curAdj);}}

我们可以往下再看frameworks/base/core/jni/android_util_Process.cpp

static const JNINativeMethod methods[] = {{"setOomAdj",   "(II)Z", (void*)android_os_Process_setOomAdj},

在这个方法中直接把adj写入/proc/pid/oom_adj中。

jboolean android_os_Process_setOomAdj(JNIEnv* env, jobject clazz,jint pid, jint adj)
{#ifdef HAVE_OOM_ADJchar text[64];sprintf(text, "/proc/%d/oom_adj", pid);int fd = open(text, O_WRONLY);if (fd >= 0) {sprintf(text, "%d", adj);write(fd, text, strlen(text));close(fd);}return true;
#endifreturn false;
}

oom_adj 这个值会同步触发同目录的oom_score_adj 会有相印的规则转换
具体可以参考源码kernel_imx/fs/proc/base.c
这个是文件操作的结构体, 我们这里看write部分

static const struct file_operations proc_oom_adj_operations = {.read        = oom_adj_read,.write      = oom_adj_write,.llseek        = generic_file_llseek,
};

写入oom_adj

static ssize_t oom_adj_write(struct file *file, const char __user *buf,size_t count, loff_t *ppos)
{//省略...//如果oom_adj是最大值15,则把oom_score_adj的最大值1000赋值给oom_adjif (oom_adj == OOM_ADJUST_MAX)oom_adj = OOM_SCORE_ADJ_MAX;else //否则oom_adj * 1000 / -(-17)oom_adj = (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
//省略...//最后把oom_adj 赋值给oom_score_adjtask->signal->oom_score_adj = oom_adj;trace_oom_score_adj_update(task);
err_sighand:unlock_task_sighand(task, &flags);
err_task_lock:task_unlock(task);put_task_struct(task);
out:return err < 0 ? err : count;
}

oom_adj 和oom_score_adj相关常量定义kernel_imx/include/uapi/linux/oom.h

/** /proc/<pid>/oom_score_adj set to OOM_SCORE_ADJ_MIN disables oom killing for* pid.*/
#define OOM_SCORE_ADJ_MIN   (-1000)
#define OOM_SCORE_ADJ_MAX   1000/** /proc/<pid>/oom_adj set to -17 protects from the oom killer for legacy* purposes.*/
#define OOM_DISABLE (-17)
/* inclusive */
#define OOM_ADJUST_MIN (-16)
#define OOM_ADJUST_MAX 15

从代码看首先 oom_adj 区间范围是-17 ~ 15;oom_score_adj区间范围是-1000 ~ 1000
转换公式:

if (oom_adj == OOM_ADJUST_MAX)oom_adj = OOM_SCORE_ADJ_MAX;
elseoom_adj = (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;

oom_adj基本是按照比例缩放到oom_score_adj的区间范围的,在lowmemorykiller里都是使用oom_score_adj这个参数来计算评分的,oom_adj 是一个旧的接口参赛,不过为了兼用,还是可以使用的,最终都会转换成oom_score_adj。

这里其实还有一个变量 oom_score,这个是干什么用的呢? 至少LMK没有使用过。
后续在要介绍Linux oom killer

curRawAdj,setRawAdj,curAdj,setAdj是什么关系?

frameworks/base/services/java/com/android/server/am/ProcessRecord.java

    int curRawAdj;              // Current OOM unlimited adjustment for this processint setRawAdj;              // Last set OOM unlimited adjustment for this processint curAdj;                 // Current OOM adjustment for this processint setAdj;                 // Last set OOM adjustment for this process

光看注释还不是很理解这四个变量是什么概念。
结合AMS的代码,在applyOomAdjLocked方法内

    private final boolean applyOomAdjLocked(ProcessRecord app, boolean wasKeeping,ProcessRecord TOP_APP, boolean doingAll, boolean reportingProcessState, long now) {boolean success = true;if (app.curRawAdj != app.setRawAdj) {//省略...app.setRawAdj = app.curRawAdj;}

setRawAdj 最终通过curRawAdj赋值的。想到与最后一次记录rawadj

        if (app.curAdj != app.setAdj) {if (Process.setOomAdj(app.pid, app.curAdj)) {if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, "Set " + app.pid + " " + app.processName +" adj " + app.curAdj + ": " + app.adjType);app.setAdj = app.curAdj;} else {success = false;Slog.w(TAG, "Failed setting oom adj of " + app + " to " + app.curAdj);}}

curAdj 最终通过setAdj赋值的。想到与最后一次记录adj。
那么curRawAdj 和curAdj 是什么关系呢?
基本通过ProcessRecord 的方法modifyRawOomAdj 将curRawAdj转换一下赋值给curAdj

curAdj = app.modifyRawOomAdj(curRawAdj);
modifyRawOomAdj方法位置:
frameworks/base/services/java/com/android/server/am/ProcessRecord.java

    int modifyRawOomAdj(int adj) {if (hasAboveClient) {// If this process has bound to any services with BIND_ABOVE_CLIENT,// then we need to drop its adjustment to be lower than the service's// in order to honor the request.  We want to drop it by one adjustment// level...  but there is special meaning applied to various levels so// we will skip some of them.if (adj < ProcessList.FOREGROUND_APP_ADJ) {// System process will not get dropped, ever} else if (adj < ProcessList.VISIBLE_APP_ADJ) {adj = ProcessList.VISIBLE_APP_ADJ;} else if (adj < ProcessList.PERCEPTIBLE_APP_ADJ) {adj = ProcessList.PERCEPTIBLE_APP_ADJ;} else if (adj < ProcessList.CACHED_APP_MIN_ADJ) {adj = ProcessList.CACHED_APP_MIN_ADJ;} else if (adj < ProcessList.CACHED_APP_MAX_ADJ) {adj++;}}return adj;}

主要看hasAboveClient这个变量

boolean hasAboveClient;     // Bound using BIND_ABOVE_CLIENT, so want to be lower

默认是false,那么相当于curRawAdj和curAdj是保持一致的,只有带BIND_ABOVE_CLIENT标签的服务连接才会变成true,从modifyRawOomAdj这个方法的值当hasAboveClient等于true的时后是期望adj数值变大, 从上面的LMK杀进程流程上看,adj数字越大代表越容易被杀死。

什么时后会killer?

这里先看看lowmem_shrink函数的注册

static struct shrinker lowmem_shrinker = {.shrink = lowmem_shrink,.seeks = DEFAULT_SEEKS * 16
};static int __init lowmem_init(void)
{register_shrinker(&lowmem_shrinker);return 0;
}static void __exit lowmem_exit(void)
{unregister_shrinker(&lowmem_shrinker);
}

主要是register_shrinker这个方法注册,他在哪里是注册的呢?
请看kernel_imx/mm/vmscan.c文件,

/** Add a shrinker callback to be called from the vm*/
void register_shrinker(struct shrinker *shrinker)
{atomic_long_set(&shrinker->nr_in_batch, 0);down_write(&shrinker_rwsem);list_add_tail(&shrinker->list, &shrinker_list);up_write(&shrinker_rwsem);
}
EXPORT_SYMBOL(register_shrinker);/** Remove one*/
void unregister_shrinker(struct shrinker *shrinker)
{down_write(&shrinker_rwsem);list_del(&shrinker->list);up_write(&shrinker_rwsem);
}
EXPORT_SYMBOL(unregister_shrinker);

后面具体触发这里还不是很清楚,后续扩展。

因为在整理的过程中有很多深入的地方我都暂时跳过去了,或者根据个人理解大概的描述了一下,如果有什么讲解的不是很清楚或者不正确的,欢迎批评指正。

Android LMK机制相关推荐

  1. 你了解Android LMK机制么?

    文章目录 前言 一.原理篇 1. 什么是Android LMK 2. OOM 3. oom_adj 的值是如何赋予的 4. LMK的工作机制 5.Android进程优先级 5.1 Android进程的 ...

  2. android lmk机制,android LMK(low memory killer) 工作机制

    Android Kernel 会定时执行一次检查,杀死一些进程,释放掉内存. 那么,如何来判断,那些进程是需要杀死的呢?答案就是我们的标题:Low memory killer机制. Low memor ...

  3. Android LMK

    2019独角兽企业重金招聘Python工程师标准>>> Android LMK 大家都知道,Android App在退出页面的时候,是不会杀死进程的,这就可能导致Memory不足.为 ...

  4. Android的LMK机制学习笔记

    初识Android的LMK机制 一.文章背景 1.1 LMK中kill进程的关键log(原生系统):![LMK中kill进程的关键log](https://img-blog.csdnimg.cn/78 ...

  5. 理解Android安全机制

    本文从Android系统架构着手,分析Android的安全机制以SE Android,最后给出一些Android安全现状和常见的安全解决方案. 1.Android系统架构 Android采用分层的系统 ...

  6. Android LowmemoryKiller机制

    为什么引入LowmemoryKiller? 进程的启动分冷启动和热启动,当用户退出某一个进程的时候,并不会真正的将进程退出,而是将这个进程放到后台,以便下次启动的时候可以马上启动起来,这个过程名为热启 ...

  7. Android消息机制Handler用法

    这篇文章介绍了Android消息机制Handler用法总结,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 1.简述 Handler消息机制主要包括: Messa ...

  8. 【腾讯Bugly干货分享】经典随机Crash之二:Android消息机制

    为什么80%的码农都做不了架构师?>>>    本文作者:鲁可--腾讯SNG专项测试组 测试工程师 背景 承上经典随机Crash之一:线程安全 问题的模型 好几次灰度top1.top ...

  9. Android刷新机制-View绘制原理

    Android刷新机制-View绘制原理 Android刷新机制-SurfaceFlinger原理 Android刷新机制-Choreographer原理 一.概述 本文将从startActivity ...

最新文章

  1. 分析 AIX 和 Linux 性能工具nmon
  2. QT项目添加现有文件后不能运行,MFC在类视图中自动隐藏类
  3. 今年美国广告程序化购买支出将超252亿美元
  4. Nginx的Gzip模块配置指令(二)
  5. HTML与CSS基础之兄弟元素(六)
  6. P1488 肥猫的游戏
  7. 交互式多模型_论文深度提升的万金油方法——多属性决策 Ⅱ
  8. java新手笔记16 面积
  9. Latex 排版第一页出现空白页
  10. javascript 小白学习指南专题 this
  11. xiaomi手机因小米账户密码遗失而无法激活手机的解决方案
  12. sql 给字段设置默认值,
  13. 2019腾讯广告大赛总结
  14. 爷青回|用Python重构【超级马里奥】制作过程+解析|快收藏起来跟小伙伴一起拯救公主吧~
  15. 邮箱登录入口官网是哪个?域名邮箱登录入口展示
  16. 【LOJ6225】【网络流24题】火星探险问题
  17. 焦作java培训_周口市转行做it
  18. gentoo 安装笔记
  19. 极米 Z7X参数 极米 Z7X评测
  20. python-DataFrame练习

热门文章

  1. win7c盘空间越来越小_技术员教你win7系统C盘空间越来越小导致C盘空间不足的设置方法...
  2. 智慧树提取期末考试试卷_常用工具软件期末考试试卷( 与参考答案)
  3. 中级会计职称考试可以带计算机么,2021年中级会计考试形式是什么?能带计算器吗?...
  4. can总线中的SOF、SRR、IDE和RTR数据位都是指什么
  5. klai安装vmware tools
  6. 如何迅速缓解皮肤过敏症状
  7. 如果你觉得你的文章没人看,请先看看这篇文章
  8. 服务器卸载重装NVIDIA显卡驱动 详细版
  9. Android 使用gif 动图
  10. 原生Javascript实现五子棋