zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    PAT (Advanced Level) Practice 1100 Mars Numbers (20分)
    PAT (Advanced Level) Practice 1107 Social Clusters (30分) (并查集)
    PAT (Advanced Level) Practice 1105 Spiral Matrix (25分)
    PAT (Advanced Level) Practice 1104 Sum of Number Segments (20分)
    PAT (Advanced Level) Practice 1111 Online Map (30分) (两次迪杰斯特拉混合)
    PAT (Advanced Level) Practice 1110 Complete Binary Tree (25分) (完全二叉树的判断+分享致命婴幼儿错误)
    PAT (Advanced Level) Practice 1109 Group Photo (25分)
    PAT (Advanced Level) Practice 1108 Finding Average (20分)
    P6225 [eJOI2019]异或橙子 树状数组 异或 位运算
    P4124 [CQOI2016]手机号码 数位DP
  • 原文地址:https://www.cnblogs.com/angeldevil/p/3090872.html
Copyright © 2011-2022 走看看