文章大纲

  • 引言
  • 一、Linux OOM Kill
  • 1、按需分配物理页面
  • 2、Memory Overcommit
  • 3、OOM Kill
  • 4、交换空间
  • 二、Android Low Memory Killer
  • 1、Low Memory Killer 概述
  • 2、lmkd 守护进程
  • 2.1、lmkd参数
  • 2.2、`lmkd` 会接收 Framework 的命令,进行相应的操作
  • 2.3、`lmkd` socket 命令处理
  • 2.4、设置进程 adj
  • 2.5、计算lowmem_oom_adj_to_oom_score_adj
  • 3、oom_odj 和oom_score_odj
  • 4、 LowMemoryKiller Kernel driver
  • 5、LowMemoryKiller 和 shrinker
  • 4.1、lowmem_shrink 触发 shrink 操作
  • 6、oom_adj与上层进程优先级的关系
  • 三、小结


引言

无论是在设备硬件条件低,内存相对稀缺的年代,还是在现在内存相对充裕,设备的内存都尤其珍贵,特别是对于一些低端设备来更甚,于是乎Linux操作系统设计了OOM Kill 机制,Android 系统也不例外。

一、Linux OOM Kill

1、按需分配物理页面

通常一个进程会申请一块很大的内存,,但只是用到其中的一小部分。为了避免内存的浪费,在分配页面时,Linux 采用的是按需分配物理页面的方式。当某个进程调用malloc()申请了一块小内存,此时内核会分配一个虚拟页面,但这个页面不会映射到实际的物理页面,因此当程序首次访问这个虚拟页面时,会触发一个缺页异常 (page fault);此时内核才会分配一个物理页面,让虚拟页面映射到这个物理页面同时更新进程的页表 (page table)。 这种按需分配物理页面的方式可以大大节省物理内存的使用,但有时会导致 Memory Overcommit。

2、Memory Overcommit

Memory Overcommit所有进程使用的虚拟内存超过了系统的物理内存和交换空间的总和。Linux 默认是允许 Memory Overcommit :

在 Linux 中,可以通过内核参数vm.overcommit_memory去控制是否允许 overcommit:

  • 默认值是 0,在这种情况下,只允许轻微的 overcommit,而比较明显的 overcommit 将不被允许。
  • 如果设置为 1,表示总是允许 overcommit。
  • 如果设置为 2,则表示总是禁止 overcommit。也就是说,如果某个申请内存的操作将导致 overcommit,那么这个操作将不会得逞。

在大多数情况下Memory Overcommit 也是安全的,因为很多进程只是申请了很多内存,但实际使用到的内存并不多。但万一很多进程都使用了申请来的大部分内存,就可能导致物理内存和交换空间不够用了,这时内核的 OOM Killer 就会出马,它会选择杀掉一个或多个进程,这样就能腾出一些内存给其它进程使用。为了让内核判断是否Memory Overcommit,Linux 设定了一个阈值 CommitLimit(若所有进程申请的总内存超过了 CommitLimit 阈值就属于Overcommit了)。可以在通过查阅/proc/meminfo文件获取 CommitLimit 的大小

yanfa204_ubuntu14-nas-18115:jdk8-181009:~$ cat /proc/meminfo | grep CommitLimit
CommitLimit: 65957864 kB

内核CommitLimit计算公式为:

//vm.overcommit_ratio为内核参数
CommitLimit = [swap size] + [RAM size] * vm.overcommit_ratio / 100

3、OOM Kill

在Linux中内存是以页面为最小单位分配的,当物理内存和交换空间不够用时,OOM Killer 就会按照一定的算法选择杀死符合条件的进程, Linux 的每个进程都有一个 oom_score (位于/proc//oom_score),这个值越大,就越有可能被 OOM Killer 选中oom_score 的值是由很多因素共同决定的:

  • 如果进程消耗的内存越大,它的 oom_score 通常也会越大。
  • 如果进程运行了很长时间,并且消耗很多 CPU 时间,那么通常它的 oom_score 会偏小。
  • 如果进程以 superuser 的身份运行,那么它的 oom_score 也会偏小。

Linux 每个进程都有一个 oom_adj (位于/proc//oom_adj),这个值的范围是 [-17, +15],进程的 oom_adj 会影响 oom_score 的计算,即我们可以通过调小进程的 oom_adj 从而降低进程的 oom_score从而降低其被OOM Killer杀死的概率。因此想要尽量避免某个进程被 OOM Killer 杀死,就可以调低它的 oom_adj 的值。

//降低 MySQL 进程的oom_adj值
$ sudo echo -10 > /proc/$(pidof mysqld)/oom_adj

4、交换空间

通常来说操作系统都会开启交换空间,因为交换空间有以下一些作用:

  • 允许系统将一些长期没有用到的物理页面换出到交换空间,这样就能节省物理内存的使用。
  • 当物理内存不够使用时,系统可以利用交换空间作为缓冲,防止一些进程因为内存不够而被 OOM Killer 杀死。

vm.swppiness可以用来配置交换空间,取值范围是 [0, 100],在 Linux 3.5 之后,它有这些作用:

  • 设置为 0 表示禁止交换空间的使用,只有当系统 OOM 时才允许使用交换空间。
  • 设置为 1 不会禁止交换空间的使用,但系统会尽量不去使用交换空间。
  • 设置为 100 表示系统会很喜欢使用交换空间。

交换空间是位于磁盘之上的,对操作系统来说,访问磁盘的速度远远慢于访问物理内存。所以我们希望,当物理内存足够使用时,系统能尽量不去使用交换空间,这样能降低页面换入换出的频率,因为频繁的页面换入换出操作会严重影响系统的性能。为了达到这种效果,我们可以把vm.swappiness设置为 1:

sudo echo 1 >  /proc/sys/vm/swappiness

二、Android Low Memory Killer

Linux OOM Killer通过一些比较复杂的评分机制,对进程进行打分,然后将分数高的进程判定为bad进程,杀死并释放内存且OOM Killer只有当系统内存不足的时候才会启动检查,而Low Memory iller 则是定时进行检查。

1、Low Memory Killer 概述

Android的Low Memory Killer基于Linux的OOM机制,通过在用户空间中指定了一组内存临界值,定时按照配置的策略检查判断进程oom_adj,当其中的某个值与进程描述中的oom_adj值在同一范围时,该进程将被Kill掉Low Memory Killer通过检查oom_adj占用的内存大小来杀死不必要的进程释放其内存。oom_adj 代表进程的优先级,数值越高,优先级越低,越容易被杀死

oom_adj的大小和进程的类型以及进程被调度的次序有关。

Android Kernel每隔一段时间会检测当前空闲内存是否低于某个阀值,假如是则杀死oom_adj最大的不必要的进程;如果有多个,就根据 oom_score_adj 去杀死进程,直到内存恢复低于阀值的状态。

2、lmkd 守护进程

Low Memory Killer 的守护进程是随着系统的启动而启动的lmkd进程。实现源码要在/system/core/lmkd/lmkd.clmkd会创建名为lmkd的socket,节点位于/dev/socket/lmkd,该socket用于跟上层framework交互。

service lmkd /system/bin/lmkd
    class core
    critical
    socket lmkd seqpacket 0660 system system
    writepid /dev/cpuset/system-background/tasks

2.1、lmkd参数

  • oom_adj:代表进程的优先级, 数值越大,优先级越低,越容易被杀. 取值范围[-16, 15]
  • oom_score_adj: 取值范围[-1000, 1000]
  • oom_score:linux oom才会使用。

2.2、lmkd 会接收 Framework 的命令,进行相应的操作

功能

命令

对应方法

LMK_PROCPRIO

设置进程adj

PL.setOomAdj()

LMK_TARGET

更新oom_adj

PL.updateOomLevels()

LMK_PROCREMOVE

移除进程

PL.remove()

2.3、lmkd socket 命令处理

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);
}

2.4、设置进程 adj

static void cmd_procprio(int pid, int uid, int oomadj) {
    struct proc *procp;
    char path[80];
    char val[20];
    ...
    snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid);
    snprintf(val, sizeof(val), "%d", oomadj);
    // 向节点/proc/<pid>/oom_score_adj写入oomadj
    writefilestring(path, val);

    // 当使用kernel方式则直接返回
    if (use_inkernel_interface)
        return;
    procp = pid_lookup(pid);
    if (!procp) {
            procp = malloc(sizeof(struct proc));
            if (!procp) {
                // Oh, the irony.  May need to rebuild our state.
                return;
            }
            procp->pid = pid;
            procp->uid = uid;
            procp->oomadj = oomadj;
            proc_insert(procp);
    } else {
        proc_unslot(procp);
        procp->oomadj = oomadj;
        proc_slot(procp);
    }
}

向节点/proc/oom_score_adj写入oom_adj。

2.5、计算lowmem_oom_adj_to_oom_score_adj

  • 当oom_adj = 15, 则 oom_score_adj = 1000;
  • 当oom_adj < 15, 则 oom_score_adj = oom_adj * 1000/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;
}

3、oom_odj 和oom_score_odj

Low Memory Killer Driver在用户空间通过两个文件保存LowMemoryKiller 的阈值的设定:

  • /sys/module/lowmemorykiller/parameters/minfree——保存着对应的内存阀值,minfree中数值代表的是内存中的页面数量(一个页面是4KB)。
  • /sys/module/lowmemorykiller/parameters/adj——代表系统将要杀掉进程对应的oom_score_adj值。

通过这两个文件指定了一组内存临界值及与之一一对应的一组oom_adj值的映射关系,可以通过修改**/sys/module/lowmemorykiller/parameters/minfree/sys/module/lowmemorykiller/parameters/adj来改变内存临界值及与之对应的oom_adj值**来定义Low Memory Killer 策略。

2,8写入节点/sys/module/lowmemorykiller/parameters/adj,再将2048,8192写入节点/sys/module/lowmemorykiller/parameters/minfree。那么相对于是告诉当系统可用内存低于8192个pages时可以杀掉oom_adj>=8的进程;当系统可用内存低于2048个pages时杀oom_adj>=2的进程。

由Low Memory Killer去指定的,对应的实现代码在**/kernel/drivers/staging/android/lowmemorykiller.c**里

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;

也可以通过init.rc自定义,比如init.rc中定义了init进程的oom_adj为-16,不可能会被杀死(init的PID是1):

on early-init
# Set init and its forked children’s oom_adj.
write /proc/1/oom_adj -16

以上的含义表示当一个进程的空闲空间下降到3 x 512个页面时,oom_adj值为0或者更大的进程会被Kill掉;当一个进程的空闲空间下降到2 x 1024个页面时,oom_adj值为10或者更大的进程会被Kill掉,依此类推。进程描述符中的signal_struct->oom_adj表示当内存短缺时进程被选择并Kill的优先级,取值范围是-17~15。如果是-17,则表示不会被选中,值越大越可能被选中。当某个进程被选中后,内核会发送SIGKILL信号将其Kill掉。实际上Low Memory Killer驱动程序会认为被用于缓存的存储空间都要被释放,但是当正常的OOM Killer被触发之前,进程是不会被Kill掉的。

4、 LowMemoryKiller Kernel driver

Low Memory Killer驱动的实现位于drivers/misc/lowmemorykiller.c,该驱动的实现非常简单,其初始化与退出操作:

static int __init lowmem_init(void)
{
    register_shrinker(&lowmem_shrinker);
    return 0;
}
static void __exit lowmem_exit(void)
{
    unregister_shrinker(&lowmem_shrinker);
}
module_init(lowmem_init);
module_exit(lowmem_exit);

在初始化函数lowmem_init中通过register_shrinker注册了一个shrinker为lowmem_shrinker,退出时又调用了函数lowmem_exit,通过unregister_shrinker来卸载被注册的lowmem_shrinker,其中lowmem_shrinker的定义如下:

static struct shrinker lowmem_shrinker = {
    .shrink = lowmem_shrink,
    .seeks = DEFAULT_SEEKS * 16
};

lowmem_shrink是这个驱动的核心实现,当内存不足时就会调用lowmem_shrink方法来Kill掉某些进程,具体实现:

static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask)
{
    struct task_struct *p;
    struct task_struct *selected = NULL;
    int rem = 0;
    int tasksize;
    int i;
    int min_adj = OOM_ADJUST_MAX + 1;
    int selected_tasksize = 0;
    int array_size = ARRAY_SIZE(lowmem_adj);
    int other_free = global_page_state(NR_FREE_PAGES);
    int other_file = global_page_state(NR_FILE_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++) {
        if (other_free < lowmem_minfree[i] &&
            other_file < lowmem_minfree[i]) {
            min_adj = lowmem_adj[i];
            break;
        }
    }
    if(nr_to_scan > 0)
        lowmem_print(3, "lowmem_shrink %d, %x, ofree %d %d, ma %d\n", nr_to_scan, 
                 gfp_mask, other_free, other_file, min_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);
    if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) {
        lowmem_print(5, "lowmem_shrink %d, %x, return %d\n", nr_to_scan, gfp_mask, 
                 rem);
        return rem;
    }

    read_lock(&tasklist_lock);
    for_each_process(p) {
        if (p->oomkilladj < min_adj || !p->mm)
            continue;
        tasksize = get_mm_rss(p->mm);
        if (tasksize <= 0)
            continue;
        if (selected) {
            if (p->oomkilladj < selected->oomkilladj)
                continue;
            if (p->oomkilladj == selected->oomkilladj &&
                tasksize <= selected_tasksize)
                continue;
        }
        selected = p;
        selected_tasksize = tasksize;
        lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n",
                     p->pid, p->comm, p->oomkilladj, tasksize);
    }
    if(selected != NULL) {
        lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n",
                     selected->pid, selected->comm,
                     selected->oomkilladj, selected_tasksize);
        force_sig(SIGKILL, selected);
        rem -= selected_tasksize;
    }
    lowmem_print(4, "lowmem_shrink %d, %x, return %d\n", nr_to_scan, gfp_mask, rem);
    read_unlock(&tasklist_lock);
    return rem;
}

首先确定我们所定义的lowmem_adj和lowmem_minfree数组的大小(元素个数)是否一致,如果不一致则以最小的为基准。因为我们需要通过比较lowmem_minfree中的空闲储存空间的值,以确定最小min_adj值(当满足其条件时,通过其数组索引来寻找lowmem_adj中对应元素的值);之后检测min_adj的值是否是初始值“OOM_ADJUST_MAX + 1”,如果是,则表示没有满足条件的min_adj值,否则进入下一步;然后使用循环对每一个进程块进行判断,通过min_adj来寻找满足条件的具体进程(主要包括对oomkilladj和task_struct进行判断);最后,对找到的进程进行NULL判断,通过“force_sig(SIGKILL, selected)”发送一条SIGKILL信号到内核,Kill掉被选中的“selected”进程。

5、LowMemoryKiller 和 shrinker

shrinker是linux kernel标准的回收内存page的机制,由内核线程kswapd负责监控。当内存不足时kswapd线程会遍历一张shrinker链表,并回调已注册的shrinker函数来回收内存page,kswapd还会周期性唤醒来执行内存操作,shrinker 链表定义如下:

struct shrinker{
    int (*shrink)(int nr_to_scan, gfp_t gfp_mask);
    int seeks;
    struct list_head list;
    long nr;
};
#define DEFAULT_SEEKS 2
extern void register_shrinker(struct shrinker *);
extern void unregiter_shrinker(struct shrinker *);

Linux里存在一个名为kswapd的内核线程,当Linux回收存放分页的时候,kswapd线程将会遍历一张shrinker链表并执行回调。

通过register_shrinker与unregister_shrinker向shrinker链表中添加或移除回调。当注册shrinker后就可以在回收内存分页时按自己定义的规则释放内存,其中lowmem_shrink为回调函数的指针。当有内存分页回收的时候,这个函数将会被回调。Android Low Memory Killer的实现在/kernel/drivers/staging/android/lowmemorykiller.c中,通过以下代码在模块初始化时注册shrinker,

static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask);
 
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);
}

module_init(lowmem_init);
module_exit(lowmem_exit);

模块加载和退出的函数,主要的功能就是register_shrinker和unregister_shrinker结构体lowmem_shrinker。主要是将函数lowmem_shrink注册到shrinker链表里,在mm_scan调用。

4.1、lowmem_shrink 触发 shrink 操作

  • 选择oom_score_adj最大的进程中,并且rss内存最大的进程作为选中要杀的进程。
  • 杀进程方式:send_sig(SIGKILL, selected, 0)向选中的目标进程发送signal 9来杀掉目标进程。
static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask)
{
        struct task_struct *p;
        struct task_struct *selected = NULL;
        int rem = 0;
        int tasksize;
        int i;
        int min_adj = OOM_ADJUST_MAX + 1;
        int selected_tasksize = 0;
        int selected_oom_adj;
        int array_size = ARRAY_SIZE(lowmem_adj);
        int other_free = global_page_state(NR_FREE_PAGES);
        int other_file = global_page_state(NR_FILE_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++) {
                if (other_free < lowmem_minfree[i] &&
                    other_file < lowmem_minfree[i]) {
                        min_adj = lowmem_adj[i];
                        break;
                }
        }
        if (nr_to_scan > 0)
                lowmem_print(3, "lowmem_shrink %d, %x, ofree %d %d, ma %d\n",
                             nr_to_scan, gfp_mask, other_free, other_file,
                             min_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);
        if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) {
                lowmem_print(5, "lowmem_shrink %d, %x, return %d\n",
                             nr_to_scan, gfp_mask, rem);
                return rem;
        }
        selected_oom_adj = min_adj;

        read_lock(&tasklist_lock);
        for_each_process(p) {
                struct mm_struct *mm;
                int oom_adj;

                task_lock(p);
                mm = p->mm;
                if (!mm) {
                        task_unlock(p);
                        continue;
                }
                oom_adj = mm->oom_adj;
                if (oom_adj < min_adj) {
                        task_unlock(p);
                        continue;
                }
                tasksize = get_mm_rss(mm);
                task_unlock(p);
                if (tasksize <= 0)
                        continue;
                if (selected) {
                        if (oom_adj < selected_oom_adj)
                                continue;
                        if (oom_adj == selected_oom_adj &&
                            tasksize <= selected_tasksize)
                                continue;
                }
                selected = p;
                selected_tasksize = tasksize;
                selected_oom_adj = oom_adj;
                lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n",
                             p->pid, p->comm, oom_adj, tasksize);
        }
        if (selected) {
                lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n",
                             selected->pid, selected->comm,
                             selected_oom_adj, selected_tasksize);
                force_sig(SIGKILL, selected);
                rem -= selected_tasksize;
        }
        lowmem_print(4, "lowmem_shrink %d, %x, return %d\n",
                     nr_to_scan, gfp_mask, rem);
        read_unlock(&tasklist_lock);
        return rem;
}

首先取得内存阈值表的大小,取阈值表数组大小与lowmem_adj_size,lowmem_minfree_size的较小值,然后通过globa_page_state获得当前剩余内存的大小,然后跟内存阈值表中的阈值相比较获得min_adj与selected_oom_adj,然后遍历所有进程找到oom_adj>min_adj并且占用内存大的进程

read_lock(&tasklist_lock);
for_each_process(p) {
    struct mm_struct *mm;
    int oom_adj;

    task_lock(p);
    mm = p->mm;
    if (!mm) {
        task_unlock(p);
        continue;
    }
    oom_adj = mm->oom_adj;
    //获取task_struct->struct_mm->oom_adj,如果小于警戒值min_adj不做处理
    if (oom_adj < min_adj) {
        task_unlock(p);
        continue;
    }
    //如果走到这里说明oom_adj>=min_adj,即超过警戒值
    //获取内存占用大小,若<=0,不做处理
    tasksize = get_mm_rss(mm);
    task_unlock(p);
    if (tasksize <= 0)
        continue;
    //如果之前已经先择了一个进程,比较当前进程与之前选择的进程的oom_adj与内存占用大小,如果oom_adj比之前选择的小或相等而内存占用比之前选择的进程小,不做处理。
    if (selected) {
        if (oom_adj < selected_oom_adj)
            continue;
        if (oom_adj == selected_oom_adj &&
            tasksize <= selected_tasksize)
            continue;
    }
    //走到这里表示当前进程比之前选择的进程oom_adj大或相等但占用内存大,选择当前进程
    selected = p;
    selected_tasksize = tasksize;
    selected_oom_adj = oom_adj;
    lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n",
                 p->pid, p->comm, oom_adj, tasksize);
}

如果选择出了符合条件的进程,发送SIGNAL信号Kill掉。

if (selected) {
    lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n",
                 selected->pid, selected->comm,
                 selected_oom_adj, selected_tasksize);
    force_sig(SIGKILL, selected);
    rem -= selected_tasksize;
}

6、oom_adj与上层进程优先级的关系

上层进程优先级按重要性依次可排列为:Foreground Progresses前台/活动进程Visiable Processes可见进程Service Progresses服务进程Background Progresses后台进程Empty Progresses空进程。详情可参见那么这些很明显这些重要性是与Low Memory Killer中的oom_adj有巨大联系的,首先在ActivityManager.RunningAppProcessInfo中我们可以看到如下关于importance的定义:

/**
 * Constant for {@link #importance}: this is a persistent process.
 * Only used when reporting to process observers.
 * @hide
 */
public static final int IMPORTANCE_PERSISTENT = 50;

/**
 * Constant for {@link #importance}: this process is running the
 * foreground UI.
 */
public static final int IMPORTANCE_FOREGROUND = 100;

/**
 * Constant for {@link #importance}: this process is running something
 * that is actively visible to the user, though not in the immediate
 * foreground.
 */
public static final int IMPORTANCE_VISIBLE = 200;

/**
 * Constant for {@link #importance}: this process is running something
 * that is considered to be actively perceptible to the user.  An
 * example would be an application performing background music playback.
 */
public static final int IMPORTANCE_PERCEPTIBLE = 130;

/**
 * Constant for {@link #importance}: this process is running an
 * application that can not save its state, and thus can't be killed
 * while in the background.
 * @hide
 */
public static final int IMPORTANCE_CANT_SAVE_STATE = 170;

/**
 * Constant for {@link #importance}: this process is contains services
 * that should remain running.
 */
public static final int IMPORTANCE_SERVICE = 300;

/**
 * Constant for {@link #importance}: this process process contains
 * background code that is expendable.
 */
public static final int IMPORTANCE_BACKGROUND = 400;

/**
 * Constant for {@link #importance}: this process is empty of any
 * actively running code.
 */
public static final int IMPORTANCE_EMPTY = 500;

这些常量表示了Process的Importance等级,而在ProcessList中我们会发现关于adj的一些定义:

// This is a process only hosting activities that are not visible,
// so it can be killed without any disruption.
static final int HIDDEN_APP_MAX_ADJ = 15;
static int HIDDEN_APP_MIN_ADJ = 9;

// The B list of SERVICE_ADJ -- these are the old and decrepit
// services that aren't as shiny and interesting as the ones in the A list.
static final int SERVICE_B_ADJ = 8;

// This is the process of the previous application that the user was in.
// This process is kept above other things, because it is very common to
// switch back to the previous app.  This is important both for recent
// task switch (toggling between the two top recent apps) as well as normal
// UI flow such as clicking on a URI in the e-mail app to view in the browser,
// and then pressing back to return to e-mail.
static final int PREVIOUS_APP_ADJ = 7;

// This is a process holding the home application -- we want to try
// avoiding killing it, even if it would normally be in the background,

// because the user interacts with it so much.
static final int HOME_APP_ADJ = 6;

// This is a process holding an application service -- killing it will not
// have much of an impact as far as the user is concerned.
static final int SERVICE_ADJ = 5;

// This is a process currently hosting a backup operation.  Killing it
// is not entirely fatal but is generally a bad idea.
static final int BACKUP_APP_ADJ = 4;

// This is a process with a heavy-weight application.  It is in the
// background, but we want to try to avoid killing it.  Value set in
// system/rootdir/init.rc on startup.
static final int HEAVY_WEIGHT_APP_ADJ = 3;

// This is a process only hosting components that are perceptible to the
// user, and we really want to avoid killing them, but they are not
// immediately visible. An example is background music playback.
static final int PERCEPTIBLE_APP_ADJ = 2;

// This is a process only hosting activities that are visible to the
// user, so we'd prefer they don't disappear.
static final int VISIBLE_APP_ADJ = 1;

// This is the process running the current foreground app.  We'd really
// rather not kill it!
static final int FOREGROUND_APP_ADJ = 0;

// This is a system persistent process, such as telephony.  Definitely
// don't want to kill it, but doing so is not completely fatal.
static final int PERSISTENT_PROC_ADJ = -12;

// The system process runs at the default adjustment.
static final int SYSTEM_ADJ = -16;

我们可以看到:

static final int PREVIOUS_APP_ADJ = 7;
static final int HOME_APP_ADJ = 6;

并不是所有的Background process的等级都是相同的,以上就是oom_adj与Importance的值的关系了。那么它们是怎么对应起来的呢?Activity实际是由ActivityManagerService来管理的,在ActivityManagerService中我们可以找到以下方法:

static int oomAdjToImportance(int adj, ActivityManager.RunningAppProcessInfo currApp) {
    if (adj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
        if (currApp != null) {
            currApp.lru = adj - ProcessList.HIDDEN_APP_MIN_ADJ + 1;
        }
        return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
    } else if (adj >= ProcessList.SERVICE_B_ADJ) {
        return ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
    } else if (adj >= ProcessList.HOME_APP_ADJ) {
        if (currApp != null) {
            currApp.lru = 0;
        }
        return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
    } else if (adj >= ProcessList.SERVICE_ADJ) {
        return ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
    } else if (adj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
        return ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE;
    } else if (adj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
        return ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
    } else if (adj >= ProcessList.VISIBLE_APP_ADJ) {
        return ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
    } else {
        return ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
    }
}

在这个方法中实现了根据adj设置importance的功能,另外我们还可以看到SERVICE还分为SERVICE_B_ADJ与SERVICE_ADJ的等级是不一样的,并不是所有Service的优先级都比Background process的优先级高。当调用Service的startForeground后,Service的importance就变为了IMPORTANCE_PERCEPTIBLE,对应的adj是PERCEPTIBLE_APP_ADJ即2,而像电话、短信等核心进程的adj为-12已基本不可能被杀死了,而在前面已经看到了,init.rc中将init进程的oom_adj设置为了-16,已经是永生进程了。

// This is a system persistent process, such as telephony.  Definitely
// don't want to kill it, but doing so is not completely fatal.
static final int PERSISTENT_PROC_ADJ = -12;

// The system process runs at the default adjustment.
static final int SYSTEM_ADJ = -16;

三、小结

以上整个Low Memory Killer 过程可以简单总结如下:

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