LMKD浅析(一)——概述

Low Memory Killer Daemon(LMKD)早在2013年被提交进AOSP代码库,其一开始就有两个部分的功能:1、基于Memory的CGroup进行进程的回收;2、作为frameworks与kernel的沟通桥梁传递参数与信息;但由于kernel始终存在lowmemorykiller驱动,因此LMKD的第一个功能始终没有被启用。

    //判断节点"/sys/module/lowmemorykiller/parameters/minfree"
    use_inkernel_interface = !access(INKERNEL_MINFREE_PATH, W_OK);

    if (use_inkernel_interface) {
        ALOGI("Using in-kernel low memory killer interface");
    } else {
        //只有节点无效时才会初始化事件监听与回调
        ret = init_mp(MEMPRESSURE_WATCH_LEVEL, (void *)&mp_event);
        if (ret)
            ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
    }

随着代码的不断完善、充实,现在的LMKD已经不仅可以完成kernel中lowmemorykiller负责的工作,并且还新增了很多可配置项,配置组合与定制性非常之高,而Google也在2018年4月在system/core/lmkd仓库中添加README.md,并宣判了lowmemorykiller的死期——kernel-4.12。即从kernel-4.12以后,内核层面将不在包括lowmemorykiller.c及其相关代码,而我们一直使用的低内存进程回收机制则会全权交由用户态的LMKD完成。

以下是system/core/lmkd/README.md的官方描述:

Android Low Memory Killer Daemon
================================


Introduction
------------

Android Low Memory Killer Daemon (lmkd) is a process monitoring memory
state of a running Android system and reacting to high memory pressure
by killing the least essential process(es) to keep system performing
at acceptable levels.


Background
----------

Historically on Android systems memory monitoring and killing of
non-essential processes was handled by a kernel lowmemorykiller driver.
Since Linux Kernel 4.12 the lowmemorykiller driver has been removed and
instead userspace lmkd daemon performs these tasks.


Android Properties
------------------

lmkd can be configured on a particular system using the following Android
properties:

  ro.config.low_ram:         choose between low-memory vs high-performance
                             device. Default = false.

  ro.lmk.use_minfree_levels: use free memory and file cache thresholds for
                             making decisions when to kill. This mode works
                             the same way kernel lowmemorykiller driver used
                             to work. Default = false

  ro.lmk.low:                min oom_adj score for processes eligible to be
                             killed at low vmpressure level. Default = 1001
                             (disabled)

  ro.lmk.medium:             min oom_adj score for processes eligible to be
                             killed at medium vmpressure level. Default = 800
                             (non-essential processes)

  ro.lmk.critical:           min oom_adj score for processes eligible to be
                             killed at critical vmpressure level. Default = 0
                             (all processes)

  ro.lmk.critical_upgrade:   enables upgrade to critical level. Default = false

  ro.lmk.upgrade_pressure:   max mem_pressure at which level will be upgraded
                             because system is swapping too much. Default = 100
                             (disabled)

  ro.lmk.downgrade_pressure: min mem_pressure at which vmpressure event will
                             be ignored because enough free memory is still
                             available. Default = 100 (disabled)

  ro.lmk.kill_heaviest_task: kill heaviest eligible task (best decision) vs.
                             any eligible task (fast decision). Default = false

  ro.lmk.kill_timeout_ms:    duration in ms after a kill when no additional
                             kill will be done, Default = 0 (disabled)

  ro.lmk.debug:              enable lmkd debug logs, Default = false

目前上游SoC厂商释放的基于Android P的代码普遍为kernel4.4与kernel4.9,这两个版本都没有剔除lowmemorykiller驱动,因此这些版本上的配置变得更为复杂。

首先撇开LMKD作为与frameworks(ProcessList.java)通信的桥梁的功能不谈,LMKD的杀进程功能在kernel中lowmemorykiller驱动工作的情况下,是不会工作的。

    //判断节点"/sys/module/lowmemorykiller/parameters/minfree"
    has_inkernel_module = !access(INKERNEL_MINFREE_PATH, W_OK);
    use_inkernel_interface = has_inkernel_module;

    if (use_inkernel_interface) {
        ALOGI("Using in-kernel low memory killer interface");
    } else {
        //只有use_inkernel_interface为假时才会初始化并注册回调
        if (!init_mp_common(VMPRESS_LEVEL_LOW) ||
            !init_mp_common(VMPRESS_LEVEL_MEDIUM) ||
            !init_mp_common(VMPRESS_LEVEL_CRITICAL)) {
            ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
            return -1;
        }
    }

要想启用LMKD的进程回收功能,那么需要做的第一件事就是禁用kernel中的lowmemorykiller驱动:

修改项目的defconfig:

#CONFIG_ANDROID_LOW_MEMORY_KILLER is not set

增量编译前记得删除out下的KERNEL_OBJ缓存。

以下针对上述文档中所列的属性作一一解释:

ro.lmk.use_minfree_levels

LMKD支持传统的基于minfree的判断机制,也支持更为客观的vmpressure,这两种判断机制通过属性ro.lmk.use_minfree_levels控制——true使用传统minfree机制,默认false使用vmpressure;

但是需要注意的是,在MTK的kernel-4.4下,使用vmpressure需要使能如下宏:

CONFIG_MEMCG=y

QCOM的kernel-4.9根据目前观察则不需要。

ro.lmk.low / ro.lmk.medium / ro.lmk.critical

这三个属性分别表示LMKD在vmpressure上报0/1/2是需要遍历并杀死进程的最小oom_adj。

例如ro.lmk.low默认1001,则表示当vmpressure==0时,不进行杀进程前的遍历行为(因为所有进程的oom_adj都小于等于1000);

ro.lmk.medium默认800,则表示当vmpressure==1时,默认从oom_adj大于等于800的进程中选择出相应进程杀死以释放足够内存空间;

ro.lmk.critical默认0,则表示当vmpressure==2时,默认从oom_adj大于等于0的进程中选择出相应进程杀死以释放足够内存空间;

ro.lmk.critical_upgrade

这个属性很鸡肋,且是否生效还取决于ro.lmk.upgrade_pressure属性的值,全程只有如下代码有效,请根据实际情况决定是否开启:

    if (enable_pressure_upgrade && level != VMPRESS_LEVEL_CRITICAL) {
        // We are swapping too much.
        // mem_pressure值越小,表示swap越多,侧面表示内存压力越大
        if (mem_pressure < upgrade_pressure) {
            level = upgrade_level(level);
            if (debug_process_killing) {
                ALOGI("Event upgraded to %s", level_name[level]);
            }
        }
    }

ro.lmk.upgrade_pressure / ro.lmk.downgrade_pressure

若想通过属性ro.lmk.upgrade_pressure与ro.lmk.downgrade_pressure来获取更为灵活的判断机制,则需要使能如下宏:

CONFIG_MEMCG_SWAP=y

ro.lmk.critical_upgrade属性的实际效果取决于ro.lmk.upgrade_pressure,但是ro.lmk.upgrade_pressure的作用并不完全取决于ro.lmk.critical_upgrade,因为除了上述代码以外ro.lmk.upgrade_pressure 与 ro.lmk.downgrade_pressure还在其他地方被LMKD用于判断是否需要杀进程;

    // If the pressure is larger than downgrade_pressure lmk will not
    // kill any process, since enough memory is available.
    if (mem_pressure > downgrade_pressure) {
        if (debug_process_killing) {
            ALOGI("Ignore %s memory pressure", level_name[level]);
        }
        return;
    } else if (level == VMPRESS_LEVEL_CRITICAL &&
               mem_pressure > upgrade_pressure) {
        if (debug_process_killing) {
            ALOGI("Downgrade critical memory pressure");
        }
        // Downgrade event, since enough memory available.
        level = downgrade_level(level);
    }

这两个属性的取值用于解决vmpressure档位过少的缺陷,通过mem_pressure来辅助判断当前内存使用是否紧张。

mem_pressure的算法如下:

    //Physical memory size used
    if ((mem_usage = get_memory_usage(&mem_usage_file_data)) < 0) {
        goto do_kill;
    }
    //Physical memory size used + Swap size
    if ((memsw_usage = get_memory_usage(&memsw_usage_file_data)) < 0) {
        goto do_kill;
    }

    // Calculate percent for swappinness.
    mem_pressure = (mem_usage * 100) / memsw_usage;

可见这个值不会大于100,越小说明swap使用越高。

ro.lmk.kill_heaviest_task
默认false - 每次需要杀进程时,则从高oom_adj开始遍历,同oom_adj时从最后加入列表的开始杀,直到释放出足够内存为止;

true - 每次需要杀进程时,则从高oom_adj开始遍历,同oom_adj时从rss最高的进程开始杀(参考节点/proc/<$pid>/statm中的第二个数值),直到释放出足够内存为止;

ro.lmk.kill_timeout_ms

默认0 - 进行过一次杀进程的操作后,间隔多少毫秒之后才会再一次执行检查与杀进程操作。

ro.lmk.debug

默认false - 当LMKD工作遇到异常情况时,是否开启一些调试log输出。

 

以上就是对LMKD的可配置参数进行了一个列举和简述,下一节会就QCOM与MTK的差异做个简单的对比,并列举更多属性之间的冲突或者协作情况。


 

  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值