Android Go是a feature of Android O MR1(8.1),它已经不再使用in kernel lowmemorykiller了,取而代之的是user space的基于memory pressure的killer。

这个user space的代码就是system/core/lmkd/lmkd.c,它是如何detect memory pressure的了?具体是在mp_event_common事件处理中:

#define MEMCG_MEMORY_USAGE "/dev/memcg/memory.usage_in_bytes"
#define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes"

static void mp_event_common(bool is_critical) {
int ret;
unsigned long long evcount;
int index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX;
int64_t mem_usage, memsw_usage;
int64_t mem_pressure;

ret = read(mpevfd[index], &evcount, sizeof(evcount));
if (ret < 0)
ALOGE("Error reading memory pressure event fd; errno=%d",
errno);

mem_usage = get_memory_usage(MEMCG_MEMORY_USAGE);
memsw_usage = get_memory_usage(MEMCG_MEMORYSW_USAGE);
if (memsw_usage < 0 || mem_usage < 0) {
find_and_kill_process(is_critical); //没内存了杀
return;
}

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

if (enable_pressure_upgrade && !is_critical) {
// We are swapping too much.
if (mem_pressure < upgrade_pressure) {
ALOGI("Event upgraded to critical.");
is_critical = true;
}
}

// 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", is_critical ? "critical" : "medium");
}
return;
} else if (is_critical && mem_pressure > upgrade_pressure) {
if (debug_process_killing) {
ALOGI("Downgrade critical memory pressure");
}
// Downgrade event to medium, since enough memory available.
is_critical = false;
}

if (find_and_kill_process(is_critical) == 0) {
if (debug_process_killing) {
ALOGI("Nothing to kill");
}
}
}

先看看memory pressure的计算,Android Go上启用了memory cgroup,具体是如下两个内核配置:

CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y

kernel3.18/Documentation/cgroups/memory.txt:

memory.usage_in_bytes # show current res_counter usage for memory
(See 5.5 for details)
memory.memsw.usage_in_bytes # show current res_counter usage for memory+Swap
(See 5.5 for details)

ok, 那基本清楚了,这个mem_pressure就和swapping成反比了,pressure值越小才说明有压力,数值越大基本没啥压力。

压力级别分成了medium和critical,相关代码都能看出:

#define MEMPRESSURE_WATCH_MEDIUM_LEVEL "medium"
#define MEMPRESSURE_WATCH_CRITICAL_LEVEL "critical"

/* memory pressure level medium event */
static int mpevfd[2];
#define CRITICAL_INDEX 1
#define MEDIUM_INDEX 0

static int medium_oomadj;
static int critical_oomadj;

回到mp_event_common,继续往下看:

如果当前swapping too much并且状态不是critical,就上升压力级别到critical,具体代码就是:

if (enable_pressure_upgrade && !is_critical) { 
// We are swapping too much.
if (mem_pressure < upgrade_pressure) {
ALOGI("Event upgraded to critical.");
is_critical = true;
}
}

如果当前没压力,也就是enough memory available时,也分成两个等级,如果内存非常充足(比downgrade_pressure还要大),啥也不用干了:

// 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", is_critical ? "critical" : "medium");
}
return;
}

如果这个没啥压力是在(upgrade_pressure, downgrade_pressure)之间,而且是critical event,那就把event级别降下到!critical。

upgrade_pressure, downgrade_pressure可配置,默认是50,60:

upgrade_pressure = (int64_t)property_get_int32("ro.lmk.upgrade_pressure", 50);
downgrade_pressure = (int64_t)property_get_int32("ro.lmk.downgrade_pressure", 60);

在build/make/target/product/go_defaults_common.mk文件中,google override了go的属性:

# Sets Android Go recommended default values for propreties.

# Set lowram options
PRODUCT_PROPERTY_OVERRIDES += \
ro.config.low_ram=true \
ro.lmk.critical_upgrade=true \
ro.lmk.upgrade_pressure=40

没有内存了,有压力了就调用find_and_kill_process找到某个进程并杀掉。

那选择的策略是什么了?in kernel是选择占用内存最多adj最大的那个,go的选择策略是:

static int find_and_kill_process(bool is_critical) {
int i;
int killed_size = 0;
int min_score_adj = is_critical ? critical_oomadj : medium_oomadj;

for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
struct proc *procp;

retry:
procp = proc_adj_lru(i);

if (procp) {
killed_size = kill_one_process(procp, min_score_adj, is_critical);
if (killed_size < 0) {
goto retry;
} else {
return killed_size;
}
}
}

return 0;
}

可见是从adj最大当中选个最近未使用的,如果压力太大adj过小的也能杀,就是把杀的范围扩大了呗,这个策略感觉明显要好于in kernel的。看了下N就这样实现了啊,难道Android很早就布局了!?