Low Memory Killer的原理
在Android中,即使當用戶退出應用程序之后,應用程序的進程也還是存在於系統中,這樣是為了方便程序的再次啟動,但是這樣的話,隨着打開的程序數量的增加,系統的內存會變得不足,就需要殺掉一部分進程以釋放內存空間。至於是否需要殺死一些進程和哪些進程需要被殺死,是通過Low Memory Killer機制來進行判定的。
Android的Low Memory Killer基於Linux的OOM機制,在Linux中,內存是以頁面為單位分配的,當申請頁面分配時如果內存不足會通過以下流程選擇bad進程來殺掉從而釋放內存:
alloc_pages -> out_of_memory() -> select_bad_process() -> badness()
在Low Memory Killer中通過進程的oom_adj與占用內存的大小決定要殺死的進程,oom_adj越小越不容易被殺死。
Low Memory Killer Driver在用戶空間指定了一組內存臨界值及與之一一對應的一組oom_adj值,當系統剩余內存位於內存臨界值中的一個范圍內時,如果一個進程的oom_adj值大於或等於這個臨界值對應的oom_adj值就會被殺掉。
可以通過修改/sys/module/lowmemorykiller/parameters/minfree與/sys/module/lowmemorykiller/parameters/adj來改變內存臨界值及與之對應的oom_adj值。minfree中數值的單位是內存中的頁面數量,一般情況下一個頁面是4KB。
比如如果向/sys/module/lowmemorykiller/parameters/adj寫入0,8,向/sys/module/lowmemorykiller/parameters/minfree中寫入1024,4096,假設一個頁面大小為4KB,這樣當系統空閑內存位於1024*4~4096*4KB之間時oom_adj大於等於8的進程就會被殺掉。
在lowmemorykiller.c中定義了閾值表的默認值,可以通過init.rc自定義:
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;
在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
在Linux中有一個kswapd的內核線程,當linux回收內存分頁的時候,kswapd線程將會遍歷一張shrinker鏈表,並執行回調,定義如下:
/* * A callback you can register to apply pressure to ageable caches. * * 'shrink' is passed a count 'nr_to_scan' and a 'gfpmask'. It should * look through the least-recently-used 'nr_to_scan' entries and * attempt to free them up. It should return the number of objects * which remain in the cache. If it returns -1, it means it cannot do * any scanning at this time (eg. there is a risk of deadlock). * * The 'gfpmask' refers to the allocation we are currently trying to * fulfil. * * Note that 'shrink' will be passed nr_to_scan == 0 when the VM is * querying the cache size, so a fastpath for that case is appropriate. */
struct shrinker { int (*shrink)(int nr_to_scan, gfp_t gfp_mask); int seeks; /* seeks to recreate an obj */ /* These are for internal use */ struct list_head list; long nr; /* objs pending delete */ }; #define DEFAULT_SEEKS 2 /* A good number if you don't know better. */ extern void register_shrinker(struct shrinker *); extern void unregister_shrinker(struct shrinker *);
通過register_shrinker與unregister_shrinker向shrinker鏈表中添加或移除回調。當注冊Shrinker后就可以在回收內存分頁時按自己定義的規則釋放內存。
Android Low Memory Killer的代碼在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);
這樣就可以在回收內存分頁時調用lowmem_shrink函數。
Low Memory Killer的實現
lowmem_shrink的定義如下:

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:
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; } }
selected_oom_adj = min_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; }
oom_adj與上層Process Importance的關系
我們知道,在上層進程按重要性可以分為:Foreground process,Visible process,Service process,Background process與Empty process,那么這些重要性怎么與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的等級都是相同的。
關於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(在記憶中曾經將Service設置為foreground並打印出其importance的值與IMPORTANCE_PERCEPTIBLE相等),對應的adj是PERCEPTIBLE_APP_ADJ,即2,已經很難被系統殺死了。
// 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;
像電話等進程的adj為-12已基本不可能被殺死了,而在前面已經看到了,init.rc中將init進程的oom_adj設置為了-16,已經是永生進程了。
相關鏈接: