【私人备忘录】Android P ActivityManagerService(六)进程管理

Android中参与进程管理的主要模块是ActivityManager,其依赖于LRU weight,OOM adj和lmkd机制(新版Low Memory Killer)共同完成进程管理。

一、LRU weight机制

LRU是Least Recently Used,最近最少使用的意思,LRU weight用于衡量最近最少使用的权重。根据关键类说明中提到,进程启动后都会在AMS的成员变量中保存其ProcessRecord信息。在AMS中mLruProcesses以LRU顺序存储了当前运行的应用程序进程信息,mLruProcesses中的第一个元素就是最近最少使用的进程对应的ProcessRecord。
    更新mLruProcesses有三个途径:
    1、程序异常退出:
    ActivityManagerService.java
    handleAppDiedLocked() → removeLruProcessLocked(app)
    2、正常干掉指定进程:
    ActivityManagerService.java
    各种kill → removeProcessLocked() → removeLruProcessLocked(app)
    3、重点关注四大组件的启动和调度(我们以启动Activity为例):
    ActivityStackSupervisor.java
    realStartActivityLocked(mService.updateLruProcessLocked(app, true, null))
    ActivityManagerService.java
    updateLruProcessLocked() → updateLruProcessInternalLocked()

    备注:
    1、ActivityManagerService.updateLruProcessLocked()主要代码:mLruSeq++(每次更新LRU列表时,LRU序号自动加1);nextIndex = updateLruProcessInternalLocked()
    2、ActivityManagerService.updateLruProcessLocked()方法只是调整了进程在mLruProcesses的位置,没有直接管理进程,在realStartActivityLocked调用mService.updateLruProcessLocked(app, true, null)之后有mService.mHandler.post(mService::updateOomAdj)来更新OOM adj信息从而影响进程管理。

二、OOM adj机制

OOM是Out Of Memory adjustment,内存不足状态的调整级别,系统给每个进程计算一个adj值(在ProcessList.java中定义,古时候是从-17到+15,现在是-900到906),adj越大越容易被干掉,人怕出名猪怕壮一样的道理。OOM adj机制中最主要的两个部分是更新OOM adj值(updateOomAdjLocked)和计算OOM adj值(computeOomAdjLocked)
    1、updateOomAdjLocked方法贼长,不过代码流程网上有很多导读的帖子,一搜一大把,不是本文重点,总结一下主要工作如下:
        根据ProcessList.CACHED_APP_MAX_ADJ和ProcessList.CACHED_APP_MIN_ADJ的值计算numSlots,并根据当前情况计算每个slot中放置的cachedProcess和emptyProcess个数;
        更新所有应用的oom_adj值,其中非cachedProcess和emptyProcess进程的oom_adj值的计算通过调用computeOomAdjLocked方法完成,而cachedProcess和emptyProcess进程则按照由近至远的方式交替从小至大的分配oom_adj值;
        对比当前cachedProcess和emptyProcess的数量是否超过这两类进程的限制值,如果超过了则调用kill方法杀掉该进程;
        对isolated process单独的处理,对于设置了isolated属性的进程为true的进程如果已经不包含服务了,则立刻kill掉该进程,最后是记录当前adj值大于等于桌面进程的所有进程数量,保存到numTrimming中;
        根据当前的cachedProcess和emptyProcess进程的数量来综合判定当前进程的等级,这两类进程的数量越少,表示系统内存越紧张,内存等级越高;
        根据当前系统内存等级,来要求进程做对应的进程内的回收。
    2、computeOomAdjLocked方法,总结一下主要工作如下:
        对于确认就是空进程的情况,直接设置对应的oom相关的信息结束计算,对于adj<=0的进程,往往我们认为该类进程都是较为重要的,设置对应的信息后也结束计算;
        设置前台进程oom相关信息,并确认adj类型;
        根据app进程中activity状态,进行oom相关信息设置,从这段逻辑中我们可以看出不同的Activity的状态都将影响着oom的计算;
        再次根据进程的状态做进一步的细分,此次操作能更多的过滤出可感知的进程;
        设置重量级进程;设置Home进程;设置前一个app进程;设置备份进程,截至目前,我们已经基本过滤出adj<=0,adj=100,200,300,400,600,700的情况,我们暂且先保存当前的adj到app.curRawAdj;
        处理进程中存在service情况设置进程oom,特别的是对于Bounded Service的情况会结合client进程设置进程的oom,需要同时考虑客户端和绑定时使用的flag;
        处理进程中provider的情况;
        确认进程的oom相关信息。

    备注:
    1、系统在Lowmemeorykill之外的回收机制是:只会对于后台进程和空进程进行回收;后台进程和空进程的系统允许的最大限制值为ActivityManagerConstants.DEFAULT_MAX_CACHED_PROCESSES常量的一半;当后台进程数或空进程数超过限制值时会对最久不使用的进程进行回收(调用kill方法),直到这两类进程数量不多于各自的进程限制值。
    2、Android系统在后台进程和空进程不超过数量上限时总是尽可能多的保留后台进程和空进程,这样用户便可再再次启动这些进程时减少启动时间从而提高了用户体验;而lowmemeorykiller的机制又会在系统可用内存不足时杀死这些进程,所以在后台进程和空进程数量少于一定数量时,便表示了系统以及触发了lowmemrorykiller的机制,而剩余的后台进程和空进程的数量则正好体现了Lowmemroykiller杀进程的程度,即表示当前系统内存的紧张程度。
    3、从代码来看当进程仅含有Unbounded Service时,整个计算过程比较单纯,只要进程没有显示过UI,且Service的存在没有超时时, 进程的oom_adj就被调整为SERVICE_ADJ。当进程含有的是Bounded Service时,整个计算的复杂度就飙升了, 它将考虑到Bound时使用的flag及客户端的情况,综合调整进程的oom_adj。从另一个角度来看,这么设计似乎也是合理的。当一个进程中的Service被许多客户端需求时,确实应该给这个进程机会,提高自己的重要性。

三、lmkd机制

Android LM Killer借鉴Linux的OOM Killer机制,OOM Killer机制只有在内存不足的时候选择杀死OOM adj值较高的进程;而LM Killer将OOM adj分级,指定每个等级最小剩余内存阈值,不仅是在应用程序分配内存发现内存不足时启动检查,它也会定时地进行检查。新时代用上了lmkd机制,主要关注ProcessList中的updateOomLevels方法和lmkd.c中的mp_event_common方法。
    1、updateOomLevels方法:
        根据系统配置计算各个等级等级对应的最小内存阈值通过writeLmkd方法修改系统文件,关键代码:
        buf.putInt(LMK_TARGET);
        buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);
        buf.putInt(mOomAdj[i]);
        方法末端有个备注:
        // GB: 2048,3072,4096,6144,7168,8192    (单位为page,默认1 page=4 KB,说明OOM adj等级为0的进程,当系统内存低于2048 * 4 / 1024 = 8 MB时候才会回收该等级的进程)
        // HC: 8192,10240,12288,14336,16384,20480
    2、mp_event_common方法:
        AMS通过socket把oom_adj及minfree传到lmkd,在lmkd中用链表把不同进程信息保存起来,当内存发生变化,kernel上报事件给lmkd,lmkd杀掉该杀的进程。需要kernel支持cgroup。
        2.1、ProcessList.updateOomLevels通过LocalSocket把不同进程处于不同状态的oom_adj及minfree值传到lmkd.c中
        2.2、lmkd.c → init() → ctrl_connect_handler() → ctrl_data_handler() → ctrl_command_handler(case LMK_TARGET) → cmd_target() 
             writefilestring(INKERNEL_MINFREE_PATH, minfreestr);
             writefilestring(INKERNEL_ADJ_PATH, killpriostr);
        2.3、lmkd.c → init() → init_mp_common() → mp_event_common() 

    备注:
    2.2中是初始化socket过程
    1、#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
    2、#define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj"
    3、ctrl_sock.sock = android_get_control_socket("lmkd")创建socket之后初始化socket最后调用ret = listen(ctrl_sock.sock, MAX_DATA_CONN);开启监听socket
    4、通过vmpressure事件通知lmkd触发mp_event_common() 
    5、查杀策略大概是:OOM adj越大的进程越容易被杀死;相同OOM adj的进程,占用内存越大的越容易被杀死;未达到最小内存阈值的最大值时,不会选择进程杀死

发布了15 篇原创文章 · 获赞 107 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/llleahdizon/article/details/90269912