美文网首页
进程状态的切换与总结

进程状态的切换与总结

作者: 我叫王菜鸟 | 来源:发表于2017-08-31 22:23 被阅读0次

    简介

    我们经常会说道Android存在几种进程状(前台进程,可见进程,服务进程,后台进程,空进程 )等等做app一般最多就是五种状态,但是具体Android系统将进程分成多少状态呢?每种状态到底有什么含义呢?今天我们就大概聊聊Android系统中的进程状态。

    ADJ级别 (1).png

    我们先来看下面这个类

    ProcessList.java


    final class ProcessList {
    

    第一阶段定义的是一些系统标注进程不同状态的一些值

    //进程crash的最小时间间隔,当一个进程连续crash的时间间隔小雨60s,这个进程就是bad进程
    static final int MIN_CRASH_INTERVAL = 60*1000;
    
    //未知进程
    static final int UNKNOWN_ADJ = 16;
    
    //如果某个进程有不可见的Activity,根据LRU原则,这个进程的adj值就会在[9,15]之间
    static final int CACHED_APP_MAX_ADJ = 15;
    static final int CACHED_APP_MIN_ADJ = 9;
    
    //最近没有任务的Service进程的adj的值
    static final int SERVICE_B_ADJ = 8;
    
    //表示previous进程的adj值(previous:之前)
    static final int PREVIOUS_APP_ADJ = 7;
    
    //Home进程的adj的值,也就是Launcher进程
    static final int HOME_APP_ADJ = 6;
    
    //service正在运行的进程
    static final int SERVICE_ADJ = 5;
    
    //重量级进程,就是android:cantSaveState属性为true的进程,目前没有用到的地方
    static final int HEAVY_WEIGHT_APP_ADJ = 4;
    
    //正在执行备份操作的进程
    static final int BACKUP_APP_ADJ = 3;
    
    //perceptible进程指的是那些没有在前台现实但是用户能感觉到比如音乐
    static final int PERCEPTIBLE_APP_ADJ = 2;
    
    //Activity的onStart()周期的进程,注意Activity弹出dialog这个activity也是可见的,只是没有焦点
    static final int VISIBLE_APP_ADJ = 1;
    
    //foreground进程,Activity在前台显示,recevire在onReceive()都是前台进程
    static final int FOREGROUND_APP_ADJ = 0;
    
    //关联着系统或persistent进程(service关联)
    static final int PERSISTENT_SERVICE_ADJ = -11;
    
    //系统persistent进程,比如telephony,这类app进程 android:persistent为true
    static final int PERSISTENT_PROC_ADJ = -12;
    
    //系统进程,但是这个值不是systemserver,这个是init进程
    //由init进程启动的daemon和service进程的adj值也等于-16
    //由于systemServer进程由Zygote启动所以Zygote=SystemServer=Init=16
    static final int SYSTEM_ADJ = -16;
    
    //native进程的adj的值
    static final int NATIVE_ADJ = -17;
    
    

    定义一些规定的常量

    //内存大小为4*1024
    static final int PAGE_SIZE = 4*1024;
    
    //只拥有activity并且activity不可见的进程最少只能是2
    static final int MIN_CACHED_APPS = 2;
    
    //允许空进程最大时间
    static final long MAX_EMPTY_TIME = 30*60*1000;
    
    //定义LMK的6个adj的值,这个和内核中的值是需要转换的
    private final int[] mOomAdj = new int[] {
            FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
           BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ
    };
    
    还会定义一些不同内存的手机对应的一些阀值
    

    基本命令

    查看adj

    可以通过命令:
    adb shell
    //筛选进程
    ps | grep <包名|pid>
    //然后,其中oom_score_adj是内核计算过后的adj
    cat proc/<pid>/oom_score_adj
    

    方法2

    adb shell
    dumpsys activity o
    //然后查看
    

    ProcessRecord中下面这些属性反应了oom_score_adj的值

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

    其中:

    • maxAdj 指定了该进程允许的oom_score_adj最大值(主要给系统应用和常驻内存使用,通过maxAdj保证这些进程拥有较高优先级)
    • curXXX这一组记录了这一次优先级计算的结果,会将curXXX复制给对应的setXXX这一组上进行备份。
    • xxxRawAdj记录了没有经过限制的adj值,“没有经过限制”是指这其中的值可能是超过了oom_score_adj文件所允许的范围(-1000 ~ 1000)
    • ProcessList.Java中预定义了oom_score_adj的可能取值。
    static final int UNKNOWN_ADJ = 1001; // 未知进程
    static final int PREVIOUS_APP_ADJ = 700; // 前一个应用
    static final int HOME_APP_ADJ = 600; // 桌面进程
    static final int SERVICE_ADJ = 500; // 包含了Service的进程
    static final int HEAVY_WEIGHT_APP_ADJ = 400; // 重量级进程
    static final int BACKUP_APP_ADJ = 300; // 备份应用进程
    static final int PERCEPTIBLE_APP_ADJ = 200; // 可感知的进程
    static final int VISIBLE_APP_ADJ = 100; // 可见进程
    static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;
    static final int FOREGROUND_APP_ADJ = 0; // 前台进程
    static final int PERSISTENT_SERVICE_ADJ = -700; // 常驻服务进程
    static final int PERSISTENT_PROC_ADJ = -800; // 常驻应用进程
    static final int SYSTEM_ADJ = -900; // 系统进程
    static final int NATIVE_ADJ = -1000; // native系统进程
    

    解释上面这些可能的情况:

    1. FOREGROUND_APP_ADJ = 0,这个是前台应用进程的优先级,正在和用户交互的进程(普通应用获得的最高优先级)
    2. VISIBLE_APP_ADJ是具有可见Activity进程的优先级,前台的Activity设置透明,或者小窗口后面的Activity也可见这种情况
    3. PERCEPTIBLE_APP_ADJ是指用户可感知的进程,可感知的进程包括:
    1.进程中包含了处于pause状态或者正在pause的Activity
    2.进程中包含了正在stop的Activity
    3.进程中包含了前台的Service
    
    1. PREVIOUS_APP_ADJ描述的是前一个应用的优先级。在启动新的Activity时,如果新启动的Activity是属于一个新的进程的,那么当前即将被stop的Activity所在的进程便会成为“前一个应用”进程。
    2. HEAVY_WEIGHT_APP_ADJ 描述的重量级进程是指那些通过Manifest指明不能保存状态的应用进程。
    3. PERSISTENT_SERVICE_ADJ = -700,PERSISTENT_PROC_ADJ = -800,系统中的一些进程,比如System_ADJ,NATIVE_ADJ=-1000

    进程分类

    • 前台进程 (Foreground process)
      • 包含用户正在交互的 Activity(已调用 Activity 的 onResume() 方法)
      • 包含某个 Service,后者绑定到用户正在交互的 Activity
      • 包含正在“前台”运行的 Service(已调用 startForeground())
      • 包含正执行一个生命周期回调的 Service(onCreate()、onStart() 或 onDestroy())
      • 包含正执行其 onReceive() 方法的 BroadcastReceiver
    • 可见进程(Visible process)
      • 不再前台,但对前台有影响,比如如果前台 Activity 启动了一个对话框,允许在其后显示上一 Activity,则有可能会发生这种情况。
      • 托管绑定到可见(或前台)Activity 的 Service
    • 服务进程 (Service process)
      • 使用startservice()方法启动服务,服务与可见内容没直接关联(下载,播放音乐等)属于可感知
    • 后台进程 (Background process)
      • 不可见的Activity进程(调用onStop()方法)这类进程保存在LRU列表中。
    • 空进程 (Empty process)
      • 不包含任何活动应用组件的进程。目的是通过应用缓存进程,而不浪费时间在启动新的进程。

    Android中进程管理的机制

    platform/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java)
    platform/frameworks/base/services/core/java/com/android/server/am/ProcessList.java
    platform/system/core/lmkd/lmkd.c
    kernel/common/drivers/staging/Android/lowmemorykiller.c
    

    通过输入命令:

    adb shell
    dumpsys activity o
    

    查看有关进程状态

    Android m

    杀进程与阶段对应

    对应在ProcessList.java中定义

    级别 常量名称 内存大小 简述
    -16 SYSTEM_ADJ 61440 kB 系统进程
    -12 PERSISTENT_PROC_ADJ 61440 kB 系统persistent进程,比如telephony
    -11 PERSISTENT_SERVICE_ADJ 61440 kB 关联着系统或persistent进程
    0 FOREGROUND_APP_ADJ 61440 kB 前台进程(Foreground process
    1 VISIBLE_APP_ADJ 76800 kB 可见进程(Visible process)
    2 PERCEPTIBLE_APP_ADJ 92160 kB 可感知进程,比如后台音乐播放
    3 BACKUP_APP_ADJ 107520 kB 备份进程
    4 HEAVY_WEIGHT_APP_ADJ 137660 kB 后台的重量级进程,system/rootdir/init.rc文件中设置
    5 SERVICE_ADJ 137660 kB 服务进程(Service process)
    6 HOME_APP_ADJ 137660 kB Home进程
    7 PREVIOUS_APP_ADJ 137660 kB 上一个App的进程(往往通过按返回键)
    8 SERVICE_B_ADJ 137660 kB List中的Service(较老的、使用可能性更小)
    9 CACHED_APP_MIN_ADJ 137660 kB 不可见进程的adj最小值
    15 CACHED_APP_MAX_ADJ 174948 kB 不可见进程的adj最大值

    Android n

    杀进程与阶段对应
    对应在ProcessList.java中定义

    级别 常量名称 内存大小 lmk杀进程根据adj6档
    -900 SYSTEM_ADJ 73,728k
    -800 PERSISTENT_PROC_ADJ 73,728K
    -700 PERSISTENT_SERVICE_ADJ 73,728K
    0 FOREGROUND_APP_ADJ 73,728K +
    100 VISIBLE_APP_ADJ 92,160K +
    200 PERCEPTIBLE_APP_ADJ 110,592K +
    300 BACKUP_APP_ADJ 129,024K +
    400 HEAVY_WEIGHT_APP_ADJ 221,184K
    500 SERVICE_ADJ 221,184K
    600 HOME_APP_ADJ 221,184K
    700 PREVIOUS_APP_ADJ 221,184K
    800 SERVICE_B_ADJ 221,184K
    900 CACHED_APP_MIN_ADJ 221,184K +
    906 CACHED_APP_MAX_ADJ 322,560K +

    Process state(m and n)

    对应在ActivityManager中定义一下几种进程的状态

    state级别 取值 解释 优先级(大于state)return IMPORTANCE_XXX
    PROCESS_STATE_CACHED_EMPTY 16 进程处于cached状态,且为空进程 400
    PROCESS_STATE_CACHED_ACTIVITY_CLIENT 15 进程处于cached状态,且为另一个cached进程(内含Activity)的client进程 400
    PROCESS_STATE_CACHED_ACTIVITY 14 进程处于cached状态,且内含Activity 400
    PROCESS_STATE_LAST_ACTIVITY 13 后台进程,且拥有上一次显示的Activity 400
    PROCESS_STATE_HOME 12 后台进程,且拥有home Activity 400
    PROCESS_STATE_RECEIVER 11 后台进程,且正在运行receiver 300
    PROCESS_STATE_SERVICE 10 后台进程,且正在运行service 300
    PROCESS_STATE_HEAVY_WEIGHT 9 后台进程,但无法执行restore,因此尽量避免kill该进程 170
    PROCESS_STATE_BACKUP 8 后台进程,正在运行backup/restore操作 130
    PROCESS_STATE_IMPORTANT_BACKGROUND 7 对用户很重要的进程,用户不可感知其存在 130
    PROCESS_STATE_IMPORTANT_FOREGROUND 6 对用户很重要的进程,用户可感知其存在 200
    PROCESS_STATE_TOP_SLEEPING 5 与PROCESS_STATE_TOP一样,但此时设备正处于休眠状态 150
    PROCESS_STATE_FOREGROUND_SERVICE 4 拥有给一个前台Service 125
    PROCESS_STATE_BOUND_FOREGROUND_SERVICE 3 拥有给一个前台Service,且由系统绑定 100
    PROCESS_STATE_TOP 2 拥有当前用户可见的top Activity 100
    PROCESS_STATE_PERSISTENT_UI 1 persistent系统进程,并正在执行UI操作 100
    PROCESS_STATE_PERSISTENT 0 persistent系统进程 100
    PROCESS_STATE_NONEXISTENT -1 不存在的进程 1000

    进程优先级定义

    AMS中有三个核心方法

    • updateOomAdjLocked:更新adj,当目标进程为空,或者被杀则返回false;否则返回true;
    • computeOomAdjLocked:计算adj,返回计算后RawAdj值;
    • applyOomAdjLocked:应用adj,当需要杀掉目标进程则返回false;否则返回true。

    updateOomAdjLocked 中会调用 computeOomAdjLocked 和 applyOomAdjLocked。

    LowMemoryKiller 的阈值的设定

    阈值的设定

    • /sys/module/lowmemorykiller/parameters/adj
    • /sys/module/lowmemorykiller/parameters/minfree
    shamu:/ # cat /sys/module/lowmemorykiller/parameters/adj
    0,100,200,300,900,906
    shamu:/ # cat /sys/module/lowmemorykiller/parameters/minfree
    18432,23040,27648,32256,36864,46080
    

    minfree中数值的单位是内存中的页面数量,一般情况下一个页面是4KB。

    例如:将1,6写入节点/sys/module/lowmemorykiller/parameters/adj,将1024,8192写入节点/sys/module/lowmemorykiller/parameters/minfree。

    策略:当系统可用内存低于8192个pages时,则会杀掉oom_score_adj>=6的进程;当系统可用内存低于1024个pages时,则会杀掉oom_score_adj>=1的进程。

    lmkd 守护进程

    system/core/lmkd/lmkd.c

    lmkd创建名称为lmkd的socket,节点位于/dev/socket/lmkd,接受命令如下:

    功能 命令 对应方法
    LMK_PROCPRIO 设置进程adj PL.setOomAdj()
    LMK_TARGET 更新oom_adj PL.updateOomLevels()
    LMK_PROCREMOVE 移除进程 PL.remove()

    设置adj

    • 向节点/proc/<pid>/oom_score_adj写入oom_adj。

    framework与lmkd对应方法:

    static void ctrl_command_handler(void) {
        int ibuf[CTRL_PACKET_MAX / sizeof(int)];
        int len;
        int cmd = -1;
        int nargs;
        int targets;
        len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX);
        if (len <= 0)
            return;
        nargs = len / sizeof(int) - 1;
        if (nargs < 0)
            goto wronglen;
        //将网络字节顺序转换为主机字节顺序
        cmd = ntohl(ibuf[0]);
        switch(cmd) {
        case LMK_TARGET:
            targets = nargs / 2;
            if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
                goto wronglen;
            cmd_target(targets, &ibuf[1]);
            break;
        case LMK_PROCPRIO:
            if (nargs != 3)
                goto wronglen;
            //设置进程adj
            cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3]));
            break;
        case LMK_PROCREMOVE:
            if (nargs != 1)
                goto wronglen;
            cmd_procremove(ntohl(ibuf[1]));
            break;
        default:
            ALOGE("Received unknown command code %d", cmd);
            return;
        }
        return;
    wronglen:
        ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len);
    }
    
    • LMK_TARGET:AMS.updateConfiguration()的过程中调用updateOomLevels()方法, 分别向/sys/module/lowmemorykiller/parameters目录下的minfree和adj节点写入相应信息;
    • LMK_PROCPRIO: AMS.applyOomAdjLocked()的过程中调用setOomAdj(),向/proc/<pid>/oom_score_adj写入oomadj 后直接返回;
    • LMK_PROCREMOVE:AMS.handleAppDiedLocked或者 AMS.cleanUpApplicationRecordLocked()的过程,调用remove(),目前不做任何事,直接返回;

    LowMemoryKiller Kernel driver

    lowmemorykiller driver 位于 drivers/staging/android/lowmemorykiller.c

    核心在于:通过 register_shrinkerunregister_shrinker分别用于初始化和退出。

    LMK通过注册shrinker来实现。其中shrinker是Linux kernel标准的回收page的机制,由内核线程kswapd负责监控。

    核心思想是

    • 选择oom_score_adj最大的进程中,并且rss内存最大的进程作为选中要杀的进程。
    • 杀进程方式:send_sig(SIGKILL, selected, 0)向选中的目标进程发送signal 9来杀掉目标进程。

    内部使用:
    lmkd参数

    • oom_adj:代表进程的优先级, 数值越大,优先级越低,越容易被杀. 取值范围[-16, 15]
    • oom_score_adj: 取值范围[-1000, 1000]
    • oom_score:lmk策略中貌似并没有看到使用的地方,这个应该是oom才会使用。

    lowmem_oom_adj_to_oom_score_adj 计算:

    //OOM_SCORE_ADJ_MAX=1000
    //OOM_DISABLE=-17
    static int lowmem_oom_adj_to_oom_score_adj(int oom_adj)
    {
        if (oom_adj == OOM_ADJUST_MAX)
            return OOM_SCORE_ADJ_MAX;
        else
            return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
    }
    
    • 当oom_adj = 15, 则 oom_score_adj = 1000;
    • 当oom_adj < 15, 则 oom_score_adj = oom_adj * 1000/17;

    小结

    • 系统framework根据不同类型进程生命周期控制,动态分配不同的adj,并在不同时机进行更新。
    • 更新adj时候在framework层会和lmkd守护进程进行通信,修改lmk deiver配置参数,设置/proc/pid/oom_score_adj;
    • lowmemorykiller 驱动会被 linux 内核的内存 shrinker 机制调度,在 shrinker 操作中,计算进程 adj 和 rss,依据 driver 的 oom_adj 和 minfree 配置,进行 kill 进程操作

    相关文章

      网友评论

          本文标题:进程状态的切换与总结

          本文链接:https://www.haomeiwen.com/subject/xzwcjxtx.html