lowmemorykiller 简称lmk,是安卓平台特有的特性,代码路径在drivers/staging/android/lowmemorykiller.c (kernel 3.18),sysfs node在/sys/module/lowmemorykiller/下,它的前身是linux OOM,主要两个参数是minfree和adj,minfree的设置主要根据framework层代码算出,但也有厂商会自己修改,要做充分的UX测试后给出。
主体扫描 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; ... }
首先看看能不能找到oom_score_adj,如果找不到就认为内存充足不杀进程,如果找到就按一定的策略选出某个进程杀掉。
判断内存是否充足的条件就是other_free和other_file两个都必须同时小于lowmem_minfree中的用户设定值,other_free基本上是free pages,other_file基本上是file pages,两者可以分别看成proc/meminfo的MemFree和Cached大小,一般来说Cached远大于MemFree。
static unsigned long lowmem_scan (struct shrinker *s, struct shrink_control *sc) { ... 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; 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) { 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; set_tsk_thread_flag(selected, TIF_MEMDIE); send_sig(SIGKILL, selected, 0 ); 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; }
遍历所有的进程for_each_process,当前任务如果是PF_KTHREAD就不能杀,如果不是用find_lock_task_mm遍历当前进程子线程,找到持有->mm
的子线程。
如果该线程有TIF_MEMDIE标记就不杀了,如果没有看下该线程的oom_score_adj要比min_score_adj小就直接continue到下一个进程如果比较大而且rss有值(>0)就作为备选后再查看下一个进程。
下一个进程必须比上一次选中的得分要大 or 得分相同但是rss比上一次的占用多的就被认为是更搓的task,要杀。
lmk是如何被调用的 [<c082a824>] (lowmem_scan) from [<c01e0ba4>] (shrink_slab_node+0x204/0x3d0) [<c01e0ba4>] (shrink_slab_node) from [<c01e12b8>] (shrink_slab+0x70/0xe4) [<c01e12b8>] (shrink_slab) from [<c01e3ba8>] (try_to_free_pages+0x3c0/0x74c) [<c01e3ba8>] (try_to_free_pages) from [<c01d9188>] (__alloc_pages_nodemask+0x578/0x92c)
static struct shrinker lowmem_shrinker = { .scan_objects = lowmem_scan, .count_objects = lowmem_count, .seeks = DEFAULT_SEEKS * 16 };
主要是回收内存流程时会调用,lowmem_scan挂到了register_shrinker里, shrink_slab_node里会scan_objects。
版权声明: 本站所有文章均采用 CC BY-NC-SA 4.0 CN 许可协议。转载请注明原文链接!