先看官方介绍

如何改用用户空间 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.