Android Low Memory Killer


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;
}
View Code

  分開來看這段代碼,首先取得內存閾值表的大小,取閾值表數組大小與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,已經是永生進程了。

 

相關鏈接:

lowermemorykiller.txt

lowermemorykiller.c

shrinker_list


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM