lowmemorykiller上面提到,trimApplications() 函数中会执行一个叫做 updateOomAdjLocked() 的函数,如果返回 false,则执行默认回收,若返回 true 则不执行默认内存回收。updateOomAdjLocked 将针对每一个进程更新一个名为 adj 的变量,并将其告知 Linux 内核,内核维护一个包含 adj 的数据结构(即进程表),并通过 lowmemorykiller 检查系统内存的使用情况,在内存不足的情况下杀死一些进程并释放内存。下面将对这种 Android Framework 与 Linux 内核相配合的内存回收机制进行研究。
由于 Android 操作系统中的所有应用程序都运行在独立的 Dalvik 虚拟机环境中,Linux 内核无法获知每个进程的运行状态,也就无法为每个进程维护一个合适的 adj 值,因此,Android Application Framework 中必须提供一套机制以动态的更新每个进程的 adj。这就是 updateOomAdjLocked()。
updateOomAdjLocked() 位于 ActivityManagerService 中,其主要作用是为进程选择一个合适的 adj 值,并通知 Linux 内核更新这个值。updateOomAdjLocked 首先调用 computeOomAdjLocked() 初步计算 adj 的值,然后回到 updateOomAdjLocked() 对其值进行进一步修正。估算流程可参见代码。
关于 adj,其定义在 task_struct->signal_struct->adj, 文件 /kernel/include/linux/sched.h 中。实质为进程数据结构中的一个变量,用来表示发生 Out of Memory 时杀死进程的优先级顺序。lowmemorykiller 利用这个变量对进程的重要程度进行判断,并在内存不足时释放部分空间,其实现在文件 /kernel/drivers/staging/android/lowmemorykiller.c 中。lowmemorykiller 定义了两个数组:lowmem_adj 和 lowmem_minfree。其中 lowmen_adj 定义了一系列 adj 键值,而 lowmem_minfree 的每个元素代表一个内存阈值。如下代码中四个阈值分别是 6MB,8MB,16MB 和 64MB,分别代表当内存小于 64MB 时,adj 大于或等于 12 的那些进程将被杀死并回收,内存小于 16MB 时,adj 大于等于 6 的那些进程将被杀死并回收,内存小于 8MB 时,adj 大于等于 1 的那些进程将被杀死并回收,内存小于 6MB 时,adj 大于等于 0 的所有进程将被杀死并回收。内核中的每个进程都持有一个 adj,取值范围 -17 到 15,值越小代表进程的重要性越高,回收优先级越低,其中 -17 代表禁用自动回收。Android 系统中,只有 0-15 被使用。
清单 8. 每个进程都持有一个 adj1
2
3
4
5
6
7
8
9
10
11
12
13
14
| static int lowmem_adj[6] = {
0,
1,
6,
12,
};
static int lowmem_adj_size = 4;
static size_t lowmem_minfree[6] = {
3 * 512, /* 6MB */
2 * 1024, /* 8MB */
4 * 1024, /* 16MB */
16 * 1024, /* 64MB */
};
static int lowmem_minfree_size = 4;
|
lowmemorykiller 注册一个 lowmem_shrinker,lowmem_shrinker 利用了标准 Linux 内核中的 Cache Shrinker 来实现,当空闲内存页面不足时,内核线程 kswapd 将用已注册的 lowmem_shrinker 来回收内存页面。
清单 9. 用已注册的 lowmem_shrinker 来回收内存页面1
2
3
4
5
6
7
8
9
10
11
| static struct shrinker lowmem_shrinker = {
.shrink = lowmem_shrink,
.seeks = DEFAULT_SEEKS * 16
};
static int __init lowmem_init(void)
{
task_free_register(&task_nb);
register_shrinker(&lowmem_shrinker);
return 0;
}
|
lowmem_shrink 的代码在函数 lowmem_shrink 中,下面给出该函数的主要结构。lowmem_shrink 根据上述规则遍历所有进程,选出需要结束的进程,通过发送一个无法忽略的信号 SIGKILL 强制结束这些进程
清单 10. 强制结束进程1
2
3
4
5
6
7
8
9
10
11
12
| static int lowmem_shrink(struct shrinker *s, int nr_to_scan, gfp_t gfp_mask)
{
for_each_process(p) {
//Select processes to be forced
}
if (selected) {
force_sig(SIGKILL, selected);
rem -= selected_tasksize;
} else
rem = -1;
return rem;
}
|
Oom_killer.如果上述各种方法都无法释放出足够的内存空间,那么当为新的进程分配应用程序时将发生 Out of Memory 异常,OOM_killer 将尽最后的努力杀掉一些进程来释放空间。Android 中的 OOM_killer 继承自标准 Linux 2.6 内核,用于分配内存时 Out of Memory 的处理。Android 并没有对其实现方式进行修改。其位置在 linux/mm/oom_kill.c。 oom_killer 遍历进程,并计算所有进程的 badness 值,选择 badness 最大的那个进程将其杀掉。函数 badness 的声明如下:
unsigned long badness(struct task_struct *p, unsigned long uptime) 函数 select_bad_process 返回将要杀掉的那个进程。
清单 11. 返回将要杀掉的进程1
2
3
4
5
6
7
8
9
10
11
12
| static struct task_struct *select_bad_process(unsigned long *ppoints,
struct mem_cgroup *mem)
{
for_each_process(p) {
points = badness(p, uptime.tv_sec);
if (points > *ppoints || !chosen) {
chosen = p;
*ppoints = points;
}
}
return chosen;
}
|
最后,和 lowmemorykiller 一样,通过发送 SIGKILL 结束选中的进程。由于 oom_killer 与标准 Linux 内核并无不同,这里不再详细研究。 |