文章大纲
- 引言
- 一、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 / 1003、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_adj4、交换空间
通常来说操作系统都会开启交换空间,因为交换空间有以下一些作用:
- 允许系统将一些长期没有用到的物理页面换出到交换空间,这样就能节省物理内存的使用。
- 当物理内存不够使用时,系统可以利用交换空间作为缓冲,防止一些进程因为内存不够而被 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.c,lmkd会创建名为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/tasks2.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 进程操作。
















