先看官方介绍

如何改用用户空间 lmkd

从 Android 9 开始,用户空间 lmkd 会在未检测到内核 lowmemorykiller 驱动程序时激活。请注意,用户空间 lmkd 要求内核支持内存 cgroup。因此,要改用用户空间 lmkd,您应使用以下配置设置编译内核:

CONFIG_ANDROID_LOW_MEMORY_KILLER=n
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y

这里以高通平台为例说明,内核版本4.9,默认使能内核态,minfree调节真是呆板,直接使用AMS计算的,改用用户态,看样子内核态lmk逐渐不用了。

高通平台修改过lmkd,增加了一个属性:

    has_inkernel_module = !access(INKERNEL_MINFREE_PATH, W_OK);
    use_inkernel_interface = has_inkernel_module && !enable_userspace_lmk;

    if (use_inkernel_interface) {
        ALOGI("Using in-kernel low memory killer interface");

    enable_userspace_lmk =
        property_get_bool("ro.lmk.enable_userspace_lmk", false);

所以高通平台下不用关心CONFIG_ANDROID_LOW_MEMORY_KILLER,只要添加属性ro.lmk.enable_userspace_lmk为true即可,策略一般选择杀死内存最大的,然后使能内核memcg:

CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y

但是,竟然起不来,从log看出来是:

No kernel memory.pressure_level support

代码在:

    mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
    if (mpfd < 0) {
        ALOGI("No kernel memory.pressure_level support (errno=%d)", errno);
        goto err_open_mpfd;
    }

看样子路径找不到,想想8.0的添加记得是在kernel cmdline里添加如下配置:

 androidboot.memcg=true

ok,加入后果然成功了,有个属性:

[ro.boot.memcg]: [true]

这个属性的代码在system/core/init/init.cpp:

    // Set memcg property based on kernel cmdline argument
    bool memcg_enabled = android::base::GetBoolProperty("ro.boot.memcg",false);
    if (memcg_enabled) {
       // root memory control cgroup
       mkdir("/dev/memcg", 0700);
       chown("/dev/memcg",AID_ROOT,AID_SYSTEM);
       mount("none", "/dev/memcg", "cgroup", 0, "memory");
       // app mem cgroups, used by activity manager, lmkd and zygote
       mkdir("/dev/memcg/apps/",0755);
       chown("/dev/memcg/apps/",AID_SYSTEM,AID_SYSTEM);
       mkdir("/dev/memcg/system",0550);
       chown("/dev/memcg/system",AID_SYSTEM,AID_SYSTEM);
    } 

找了半天没发现ro.boot.memcg怎么根据androidboot.memcg设置的,想了想应该是拼接的,如果给你实现你怎么做?具体设置是在:

static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
    if (key.empty()) return;

    if (for_emulator) {
        // In the emulator, export any kernel option with the "ro.kernel." prefix.
        property_set("ro.kernel." + key, value);
        return;
    }   

    if (key == "qemu") {
        strlcpy(qemu, value.c_str(), sizeof(qemu));
    } else if (android::base::StartsWith(key, "androidboot.")) {
        property_set("ro.boot." + key.substr(12), value); //tj: here
    }   
}

这里设置了ro.boot.memcg为true,来自init main里会解析kernel cmdline:

    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.
    process_kernel_dt();
    process_kernel_cmdline();
static void process_kernel_cmdline() {
    // The first pass does the common stuff, and finds if we are in qemu.
    // The second pass is only necessary for qemu to export all kernel params
    // as properties.
    import_kernel_cmdline(false, import_kernel_nv);
    if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
}

Done.