Android 应用如果需要常驻后台运行且不能被杀,通常的办法是增加persist属性,并需要是系统预装的应用或者有系统签名。这样也不能完全保证lowmemkiller机制不会杀此应用,原因是 应用升级后(即此应用之前的版本persist属性是起作用的,新版本也是携带此属性的),应用的persist属性会自然失效,这是由android的机制决定的(下一篇文章解释)。此时新版本的应用就和普通的应用没有什么区别,随时会被Lowmemkill模块杀死,而不能达到常驻后台的需求。
这里提供一种可能的解决方案,我们先看lowmem的内存管理的机制,代码如下:
static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
{
struct task_struct *tsk;
struct task_struct *selected = NULL;
unsigned long rem = 0;
int tasksize;
int i;
short min_score_adj = OOM_SCORE_ADJ_MAX + 1;
int selected_tasksize = 0;
short selected_oom_score_adj;
int array_size = ARRAY_SIZE(lowmem_adj);
int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages;
int other_file = global_page_state(NR_FILE_PAGES) -
global_page_state(NR_SHMEM) -
total_swapcache_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_score_adj = lowmem_adj[i];
break;
}
}
lowmem_print(3, "lowmem_scan %lu, %x, ofree %d %d, ma %hd\n",
sc->nr_to_scan, sc->gfp_mask, other_free,
other_file, min_score_adj);
if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
lowmem_print(5, "lowmem_scan %lu, %x, return 0\n",
sc->nr_to_scan, sc->gfp_mask);
return 0;
}
selected_oom_score_adj = min_score_adj;
rcu_read_lock();
for_each_process(tsk) {
struct task_struct *p;
short oom_score_adj;
if (tsk->flags & PF_KTHREAD)/*如果是内核线程,则直接跳过*/
continue;
p = find_lock_task_mm(tsk); /**如果是内存不相关的进程,也跳过**/
if (!p)
continue;
if (test_tsk_thread_flag(p, TIF_MEMDIE) &&
time_before_eq(jiffies, lowmem_deathpending_timeout)) {
task_unlock(p);
rcu_read_unlock();
return 0;
}
oom_score_adj = p->signal->oom_score_adj; /**获取oom_score_adj**/
if (oom_score_adj < min_score_adj) {
task_unlock(p);
continue;
}
tasksize = get_mm_rss(p->mm);
task_unlock(p);
if (tasksize <= 0)
continue;
if (selected) {
if (oom_score_adj < selected_oom_score_adj)
continue;
if (oom_score_adj == selected_oom_score_adj &&
tasksize <= selected_tasksize)
continue;
}
selected = p;
selected_tasksize = tasksize;
selected_oom_score_adj = oom_score_adj;
lowmem_print(2, "select %d (%s), adj %hd, size %d, to kill\n",
p->pid, p->comm, oom_score_adj, tasksize);
}
if (selected) {
task_lock(selected);
send_sig(SIGKILL, selected, 0);
/*
* FIXME: lowmemorykiller shouldn't abuse global OOM killer
* infrastructure. There is no real reason why the selected
* task should have access to the memory reserves.
*/
if (selected->mm)
mark_oom_victim(selected);
task_unlock(selected);
lowmem_print(1, "send sigkill to %d (%s), adj %hd, size %d\n",
selected->pid, selected->comm,
selected_oom_score_adj, selected_tasksize);
lowmem_deathpending_timeout = jiffies + HZ;
rem += selected_tasksize;
}
lowmem_print(4, "lowmem_scan %lu, %x, return %lu\n",
sc->nr_to_scan, sc->gfp_mask, rem);
rcu_read_unlock();
return rem;
}
从以上代码可以看出,Lowmemkiller的本质是遍历系统进程的oom_adj的值并结合占用内存的大小,选择合适的进程进行kill的。它首先排除了内核进程(即标记为PF_KTHREAD)的进程,然后按照oom_adj的大小进行选择,并结束进程的。这样就给我们提供了一种可能的方案,通过其他手段来直接修改
应用进程对应的oom_adj数值,使得Lowmemkiller模块按照既有的算法跳过我们的应用进程,即通过修改(需要特别通道支持):
/proc/进程号/oom_adj
/proc/进程号/oom_score_adj
的值为负数,通常为-11(系统针对persist属性应用设置的oom_adj的值),通常是在应用启动时和应用的lowmem回调方法中来设置,以防止应用被lowmemkiller杀死。此方法能满足绝大多数的情况,因为系统也会不定时的更新进程的oom_adj,所以此方案并不能完全避免被系统kill,但是基本能解决应用自升级到新版本后被Lowmemkiller杀死的可能.