Android底层基于Linux内核开发.随着Android版本不断更新,内存回收机制也在不断变化.本文简要介绍下不同版本下的内存回收原理.
Linux OOM机制
OOM(out of memory)是linux中内存管理机制的一种,在系统可用内存较少的情况下,内核为了保证系统还能够继续运行下去,会选择杀掉一些进程释放掉一些内存.通常oom_killer的触发流程是: 进程A想要分配物理内存->粗发缺页异常-> 内核去分配物理内存-> 物理内存不足-> OOM. 当OOM发生时,可以有两种选择:
- kernelpanic(死机)
- 启动oom_killer,遍历当前所有进程,根据进程的内存使用情况进行打分,然后从中选择一个分数最高进程杀掉,从而回收内存
主要处理流程
调用oom_killer前,系统会对oom_control做一个填充:
pagefault_out_of_memory(void)
{
struct oom_control oc = {
.zonelist = NULL,
.nodemask = NULL,
.memcg = NULL,
.gfp_mask = 0,
.order = 0,
};
...
out_of_memory(&oc);
}
oom_killer的处理主要集中在mm/oom_kill.c
核心函数为out_of_memory
bool out_of_memory(struct oom_control *oc)
{
unsigned long freed = 0;
enum oom_constraint constraint = CONSTRAINT_NONE;
if (oom_killer_disabled)
return false;
if (!is_memcg_oom(oc)) {
blocking_notifier_call_chain(&oom_notify_list, 0, &freed);
if (freed > 0)
/* Got some memory back in the last second. */
return true;
}
/*
* If current has a pending SIGKILL or is exiting, then automatically
* select it. The goal is to allow it to allocate so that it may
* quickly exit and free its memory.
*/
if (task_will_free_mem(current)) {
mark_oom_victim(current);
wake_oom_reaper(current);
return true;
}
/*
* The OOM killer does not compensate for IO-less reclaim.
* pagefault_out_of_memory lost its gfp context so we have to
* make sure exclude 0 mask - all other users should have at least
* ___GFP_DIRECT_RECLAIM to get here.
*/
if (oc->gfp_mask && !(oc->gfp_mask & __GFP_FS))
return true;
/*
* Check if there were limitations on the allocation (only relevant for
* NUMA and memcg) that may require different handling.
*/
constraint = constrained_alloc(oc);
if (constraint != CONSTRAINT_MEMORY_POLICY)
oc->nodemask = NULL;
check_panic_on_oom(oc, constraint);
if (!is_memcg_oom(oc) && sysctl_oom_kill_allocating_task &&
current->mm && !oom_unkillable_task(current, NULL, oc->nodemask) &&
current->signal->oom_score_adj != OOM_SCORE_ADJ_MIN) {
get_task_struct(current);
oc->chosen = current;
oom_kill_process(oc, "Out of memory (oom_kill_allocating_task)");
return true;
}
select_bad_process(oc);
/* Found nothing?!?! Either we hang forever, or we panic. */
if (!oc->chosen && !is_sysrq_oom(oc) && !is_memcg_oom(oc)) {
dump_header(oc, NULL);
panic("Out of memory and no killable processes...\n");
}
if (oc->chosen && oc->chosen != (void *)-1UL) {
oom_kill_process(oc, !is_memcg_oom(oc) ? "Out of memory" :
"Memory cgroup out of memory");
/*
* Give the killed process a good chance to exit before trying
* to allocate memory again.
*/
schedule_timeout_killable(1);
}
return !!oc->chosen;
}
处理流程:
- 通知系统中注册了oom_nofiy_list的模块释放了内存,如果这些内存从模块中释放了一些内存,那么直接结束omm killer进程,回收失败,则进入下一步omm_killer;
- 触发OOM_killer通常是由当前进程进行内存分配引起,而如果当前进程已经挂起了一个SIG_KILL信号,直接选中当前进程,否则进入下一步
- check_panic_on_oom检查系统管理员设置,看oom时是直接panic还是进行OOM_killer.
- 如果管理原规定,谁引起oom,kill谁,那么就直接杀掉正在尝试分配内存的进程
sysctl_oom_kill_allocating_task
- 调用select_bad_process选择合适的进程,然后调用OOM_kill_process杀死选中进程.如果没有找到,则触发panic
sysctl_panic_on_oom
该参数在check_panic_on_oom函数中引用,当参数等于0时,启动OOM killer.当参数等于2时,如果不是sysrq进程的话,强制进入kernel panic.等于其他值时,要分具体情况,对于某些情况可以panic,有些情况启动oom killer.
kernel代码中,enum oom_constraint
就是一个进一步描述oom状态的参数.定义如下:
enum oom_constraint {
CONSTRAINT_NONE,
CONSTRAINT_CPUSET,
CONSTRAINT_MEMORY_POLICY,
CONSTRAINT_MEMCG,
};
对于UMA而言,oom_constrain永远都是CONSTRAINT_NONE,表示系统并没有什么约束就出现了oom.
在NUMA下,有可能附加了其他的约束导致了系统遇到OOM状态,实际上,系统中还有充足的内存.这些约束包括:
OCNSTRAINT_CPUSET
cpusets是kernel中的一种机制,通过该机制可以把一组cpu和memory node资源分配给特定的一组进程.这时候如果出现OOM,仅仅说明该进程能分配memory的那个node出现状况了,整个系统有很多的memory node,其他的node可能有充足的memory资源.
CONSTRAINT_MEMORY_POLICY
memory policy是NUMA系统中如何控制分配各个memory node资源的策略模块。用户空间程序(NUMA-aware的程序)可以通过memory policy的API,针对整个系统、针对一个特定的进程,针对一个特定进程的特定的VMA来制定策略。产生了OOM也有可能是因为附加了memory policy的约束导致的,在这种情况下,如果导致整个系统panic似乎有点不太合适。
CONSTRAINT_MEMCG
MEMCG就是memory control group,Cgroup中的memory子系统就是控制系统memory资源分配的控制器,通俗的将就是把一组进程的内存使用限定在一个范围内。当这一组的内存使用超过上限就会OOM,在这种情况下的OOM就是CONSTRAINT_MEMCG类型的OOM。
select_bad_process
该函数从系统中选择一个合适被杀死的进程,对系统关键进程不能杀死,其他则通过 oom_badness 进行打分,分数最高者被选中:
oom_badness
/**
* oom_badness - heuristic function to determine which candidate task to kill
* @p: task struct of which task we should calculate
* @totalpages: total present RAM allowed for page allocation
*
* The heuristic for determining which task to kill is made to be as simple and
* predictable as possible. The goal is to return the highest value for the
* task consuming the most memory to avoid subsequent oom failures.
*/
unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
const nodemask_t *nodemask, unsigned long totalpages)
{
long points;
long adj;
...
adj = (long)p->signal->oom_score_adj;
if (adj == OOM_SCORE_ADJ_MIN ||
test_bit(MMF_OOM_SKIP, &p->mm->flags) ||
in_vfork(p)) {
task_unlock(p);
return 0;
}
/*
* The baseline for the badness score is the proportion of RAM that each
* task's rss, pagetable and swap space use.
*/
points = get_mm_rss(p->mm) + get_mm_counter(p->mm, MM_SWAPENTS) +
atomic_long_read(&p->mm->nr_ptes) + mm_nr_pmds(p->mm);
task_unlock(p);
/*
* Root processes get 3% bonus, just like the __vm_enough_memory()
* implementation used by LSMs.
*/
if (has_capability_noaudit(p, CAP_SYS_ADMIN))
points -= (points * 3) / 100;
/* Normalize to oom_score_adj units */
adj *= totalpages / 1000;
points += adj;
/*
* Never return 0 for an eligible task regardless of the root bonus and
* oom_score_adj (oom_score_adj can't be OOM_SCORE_ADJ_MIN here).
*/
return points > 0 ? points : 1;
}
详细介绍下oom_badness主要工作:
代码17行
对某一个task进行打分(oom_score)主要由两部分组成:
系统打分,主要是根据该task的内存使用情况
用户打分,即oom_score_adj
该task的实际得分需要综合两方面的打分.如果用户将task的oom_socre_adj设置成OOM_SCORE_ADJ_MIN(-1000)的话,实际上就是禁止了oom killer杀死该进程.
返回0,即通知oom killer,该进程是"good process". 后面可以看到实际计算分数时最低分是1分.
代码27行
系统打分就是看物理内存消耗量,主要是三部分:RSS,swap fille或者swap device上占用的内存情况,页表占用的内存情况.
代码35行
root进程有3%的内存室友特权,因此这里要减去那些内存使用量
代码39行
用户可以调整oom_score,具体操作方法如下:
Android system
oom_score_adj的取值范围是-1000~1000,0表示用户不调整oom_score,负值表示要在实际打分值上减去一个折扣,正值表示要惩罚该task,也就是增加该进程的oom_score。在实际操作中,需要根据本次内存分配时候可分配内存来计算(如果没有内存分配约束,那么就是系统中的所有可用内存,如果系统支持cpuset,那么这里的可分配内存就是该cpuset的实际额度值)。oom_badness函数有一个传入参数totalpages,该参数就是当时的可分配的内存上限值。实际的分数值(points)要根据oom_score_adj进行调整,例如如果oom_score_adj设定-500,那么表示实际分数要打五折(基数是totalpages),也就是说该任务实际使用的内存要减去可分配的内存上限值的一半。
Android kernel LMK机制
在Android中,及时用户退出当前应用程序后,应用程序还是会存在于系统当中,这是为了方便程序的再次启动。但是这样的话,随着打开的程序的数量的增加,系统的内存就会不足,从而需要杀掉一些进程来释放内存空间。至于是否需要杀进程以及杀什么进程,这个就是由Android的内部机制LowMemoryKiller机制来进行的。
Andorid的Low Memory Killer是在标准的linux lernel的OOM基础上修改而来的一种内存管理机制。当系统内存不足时,杀死不必要的进程释放其内存。不必要的进程的选择根据有2个:oom_adj和占用的内存的大小。oom_adj代表进程的优先级,数值越高,优先级月低,越容易被杀死;对应每个oom_adj都可以有一个空闲进程的阀值。Android Kernel每隔一段时间会检测当前空闲内存是否低于某个阀值。假如是,则杀死oom_adj最大的不必要的进程,直到内存恢复低于阀值的状态。
对比下oom
名称 | 触发条件 |
---|---|
OOM | 某进程申请内存时发生页面错误,没有足够剩余的内存可供分配 |
Lowmemorykiller | 定期扫描系统内存压力情况,低于某个阈值后,就正式启动回收. |
LMK初始化
初始化主要为kobject注册和各通知链的注册
static int __init lowmem_init(void)
{
rc = kobject_init_and_add(lowmem_notify_kobj, &lowmem_notify_kobj_type,
mm_kobj, "lowmemkiller");
register_shrinker(&lowmem_shrinker);
#ifdef CONFIG_OOM_NOTIFIER
register_oom_notifier(&android_oom_notifier);
#endif
#ifdef CONFIG_E_SHOW_MEM
register_e_show_mem_notifier(&tasks_e_show_mem_notifier);
#endif
vmpressure_notifier_register(&lmk_vmpr_nb);
nl_sk = netlink_kernel_create(&init_net, LMK_NETLINK_PROTO, &cfg);
}
其中lowmen_shrinker定义如下:
static struct shrinker lowmem_shrinker = {
.scan_objects = lowmem_scan,
.count_objects = lowmem_count,
.seeks = DEFAULT_SEEKS * 16,
.flags = SHRINKER_LMK
};
static short lowmem_adj[6] = {
0,
1,
6,
12,
};
static int lowmem_minfree[6] = {
3 * 512, /* 6MB */
2 * 1024, /* 8MB */
4 * 1024, /* 16MB */
16 * 1024, /* 64MB */
};
lowmem_adj这个数据在系统运行中会有填充.具体数值可在如下节点获取:
/sys/module/lowmemorykiller/parameters/minfree:里面是以”,”分割的一组数,每个数字代表一个内存级别 /sys/module/lowmemorykiller/parameters/adj:对应上面的一组数,每个数组代表一个进程优先级级别
sp9832e_1h10:/sys/module/lowmemorykiller/parameters # cat minfree ; cat adj 18432,23040,27648,32256,55296,80640 0,100,200,300,900,906
代表的意思:两组数一一对应,当手机内存低于80640时,就去杀掉优先级906以及以上级别的进程,当内存低于55296时,就去杀掉优先级900以及以上的进程。
内存回收功能实现主要在lowmem_scan函数中:
static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
{
/* work around for antutu */
struct task_struct *selected_antutu = NULL;
int selected_antutu_tasksize = 0;
short selected_antutu_adj = -1000;
bool has_antutu_3D = false;
#ifdef CONFIG_LOWMEM_NOTIFY_KOBJ
lowmem_notif_sc.gfp_mask = sc->gfp_mask;
if (get_free_ram(&other_free, &other_file_orig, &other_file, sc)) {
if (mutex_is_locked(&kernfs_mutex))
msleep(1);
if (!mutex_is_locked(&kernfs_mutex))
lowmem_notify_killzone_approach();
else
lowmem_print(1, "skip as kernfs_mutex is locked.");
}
#else
get_current_ram(&other_free, &other_file_orig, &other_file, sc);
#endif
for (i = 0; i < array_size; i++) {
minfree = lowmem_minfree[i];
if (other_free < minfree && other_file < minfree) {
min_score_adj = lowmem_adj[i];
break;
}
}
ret = adjust_minadj(&min_score_adj, &pressure);
selected_oom_score_adj = min_score_adj;
for_each_process(tsk) {
struct task_struct *p;
short oom_score_adj;
if (tsk->flags & PF_KTHREAD)
continue;
if (time_before_eq(jiffies, lowmem_deathpending_timeout)) {
if (test_task_flag(tsk, TIF_MEMDIE)) {
rcu_read_unlock();
mutex_unlock(&scan_mutex);
return 0;
}
}
/* workaround for antutu */
if (strstr("com.antutu.benchmark.full", p->comm))
has_antutu_3D = true;
oom_score_adj = p->signal->oom_score_adj;
if (oom_score_adj < min_score_adj) {
task_unlock(p);
continue;
}
tasksize = get_mm_rss(p->mm);
task_unlock(p);
if (tasksize <= 0)
continue;
if (selected) {
if (oom_score_adj < selected_oom_score_adj)
continue;
if (oom_score_adj == selected_oom_score_adj &&
tasksize <= selected_tasksize)
continue;
}
/* workaround for antutu */
if (!selected_antutu &&
strstr("com.antutu.ABenchMark", p->comm)) {
selected_antutu = p;
selected_antutu_tasksize = tasksize;
selected_antutu_adj = oom_score_adj;
continue;
}
selected = p;
selected_tasksize = tasksize;
selected_oom_score_adj = oom_score_adj;
lowmem_print(2, "select '%s' (%d), adj %hd, size %d, to kill\n",
p->comm, p->pid, oom_score_adj, tasksize);
}
/* workaround for antutu:
* if 3D task is not exist, check if the antutu task is more suited
* to be killed
*/
if (selected && selected_antutu && !has_antutu_3D) {
if (selected_antutu_adj > selected_oom_score_adj ||
(selected_antutu_adj == selected_oom_score_adj &&
selected_antutu_tasksize > selected_tasksize)) {
selected = selected_antutu;
selected_tasksize = selected_antutu_tasksize;
selected_oom_score_adj = selected_antutu_adj;
}
}
if (selected) {
long cache_size = other_file * (long)(PAGE_SIZE / 1024);
long cache_size_orig = other_file_orig * (long)(PAGE_SIZE / 1024);
long cache_limit = minfree * (long)(PAGE_SIZE / 1024);
long free = other_free * (long)(PAGE_SIZE / 1024);
if (test_task_flag(selected, TIF_MEMDIE) &&
(test_task_state(selected, TASK_UNINTERRUPTIBLE))) {
lowmem_print(2, "'%s' (%d) is already killed\n",
selected->comm,
selected->pid);
rcu_read_unlock();
mutex_unlock(&scan_mutex);
return 0;
}
task_lock(selected);
/* add for lmfs */
selected_process_uid = from_kuid(&init_user_ns,
selected->cred->uid);
selected_process_pid = selected->pid;
selected_process_adj = selected_oom_score_adj;
send_sig(SIGKILL, selected, 0);
/*
* FIXME: lowmemorykiller shouldn't abuse global OOM killer
* infrastructure. There is no real reason why the selected
* task should have access to the memory reserves.
*/
if (selected->mm)
mark_oom_victim(selected);
task_unlock(selected);
trace_lowmemory_kill(selected, cache_size, cache_limit, free);
si_swapinfo(&si);
lowmem_deathpending_timeout = jiffies + HZ;
rem += selected_tasksize;
trace_almk_shrink(selected_tasksize, ret,
other_free, other_file, selected_oom_score_adj);
} else {
trace_almk_shrink(1, ret, other_free, other_file, 0);
}
if (selected) {
send_killing_app_info_to_user(selected_process_uid,
selected_process_pid,
selected_process_adj);
return rem;
}
Android LMKD 机制
下面介绍Android 9 中新增的用户空间 lowmemorykiller 守护进程 (lmkd
) 功能及其配置方法
代码位置 platform/system/core/lmkd/
过去,Android 使用内核中的 lowmemorykiller 驱动程序来缓解内存压力(通过终止非必需进程)。此机制非常严格,具体取决于硬编码值。此外,从内核版本 4.12 开始,lowmemorykiller 驱动程序会从上游内核中排除。
用户空间 lmkd
进程可实现相同的功能,但它是通过现有的内核机制来检测和估测内存压力。该进程使用内核生成的 vmpressure 事件来获取关于内存压力级别的通知。此外,它还可以使用内存 cgroup 功能来限制分配给相应进程的内存资源(根据每个进程的重要性)
ProcessList中定义有进程的优先级,越重要的进程的优先级越低,前台APP的优先级为0,系统APP的优先级一般都是负值,所以一般进程管理以及杀进程都是针对与上层的APP来说的,而这些进程的优先级调整都在AMS里面,AMS根据进程中的组件的状态去不断的计算每个进程的优先级,计算之后,会及时更新到对应进程的文件节点中,而这个对文件节点的更新并不是它完成的,而是lmkd,他们之间通过socket通信。
lmkd在手机中是一个常驻进程,用来处理上层ActivityManager在进行updateOomAdj之后,通过socket与lmkd进行通信,更新进程的优先级,如果必要则杀掉进程释放内存。lmkd是在init进程启动的时候启动的,在lmkd中有定义lmkd.rc:
service lmkd /system/bin/lmkd
class core
group root readproc
critical
socket lmkd seqpacket 0660 system system
socket lmfs stream 0660 root system
socket vmpressure stream 0666 root system
writepid /dev/cpuset/system-background/tasks
配置内核以支持LMKD
从 Android 9 开始,用户空间 lmkd
会在未检测到内核 lowmemorykiller 驱动程序时激活。请注意,用户空间 lmkd
要求内核支持内存 cgroup。因此,要改用用户空间 lmkd
,您应使用以下配置设置编译内核:
CONFIG_ANDROID_LOW_MEMORY_KILLER=n
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y
LMKD 终止策略
lmkd
支持基于以下各项的新终止策略:
- vmpressure event
- severity
- 其他提示(如交换利用率
swap utilization
) - 旧模式(在该模式下,
lmkd
会像内核 lowmemorykiller 驱动程序一样做出终止决策)。
内存不足的设备和高性能设备的新终止策略有所不同。对于内存不足的设备,一般情况下,系统会选择承受较大的内存压力;对于高性能设备,如果存在内存压力,则属于异常情况,应及时修复,以免影响整体性能。ro.config.low_ram
属性允许选择其中一种模式。有关如何设置此属性的说明,请参阅低内存配置。
在旧模式下,lmkd
终止决策是基于可用内存和文件缓存阈值做出的。您可以将 ro.lmk.use_minfree_levels
属性设置为 true
,从而启用此模式。
为特定设备配置LMKD
属性 | 使用情况 | 默认值 |
---|---|---|
ro.config.low_ram | 在内存不足的设备和高性能设备之间进行选择。 | false |
ro.lmk.use_minfree_levels | 使用可用内存和文件缓存阈值来决定何时终止。此模式与内核 lowmemorykiller 驱动程序之前的工作原理相同。 | false |
ro.lmk.low | 可在低 vmpressure 级别下被终止的进程的最低 oom_adj 得分。 | 1001 (已停用) |
ro.lmk.medium | 可在中等 vmpressure 级别下被终止的进程的最低 oom_adj 得分。 | 800 (已缓存或非必需服务) |
ro.lmk.critical | 可在临界 vmpressure 级别下被终止的进程的最低 oom_adj 得分。 | 0 (任何进程) |
ro.lmk.critical_upgrade | 能够升级到临界级别。 | false |
ro.lmk.upgrade_pressure | 由于系统交换次数过多,将在该级别升级 vmpressure 事件的 mem_pressure 上限。 | 100 (已停用) |
ro.lmk.downgrade_pressure | 由于仍有足够的可用内存,将在该级别忽略 vmpressure 事件的 mem_pressure* 下限。 | 100 (已停用) |
ro.lmk.kill_heaviest_task | 终止符合条件的最重要任务(最佳决策)与任何符合条件的任务(快速决策)。 | true |
ro.lmk.kill_timeout_ms | 从某次终止后到其他终止完成之前的持续时间(以毫秒为单位)。 | 0 (已停用) |
ro.lmk.debug | 启用 lmkd 调试日志。 |
false |
*注意:*mem_pressure = 内存使用量/RAM_and_swap 使用量(以百分比的形式表示)
low level 正常回收;medium level就开始swaping;critical就是快没内存了
具体实现
AMS与LMKD通信command
主要分为五种,每种command代表一种数据控制方式,在ProcessList及lmkd中都有定义:
LMK_TARGET:更新/sys/module/lowmemorykiller/parameters/中的minfree及adj
LMK_PROCPRIO:更新指定进程的优先级,也就是oom_socre_adj
LMK_PROCREMOVE:移除进程
Purge:清理所有注册的进程
LMK_GETKILLCNT:获取kill的进程数
数据结构
用来描述handle events的数据结构
struct event_handler_info {
int data;
void (*handler)(int data, uint32_t events);
};
定义描述vmpressure event的结构体变量: vmpressure_hinfo
用来描述socket events的数据结构
struct sock_event_handler_info {
int sock;
struct event_handler_info handler_info;
};
用来定义了两个数据: ctrl_sock
data_sock
table,类似hashtable,不过计算index的方式不是hash,而是oom_score_adj经过转换后直接作为index.数组的每个元素都是双向循环链表进程的优先级作为数组的index.即以进程的优先级为index,从-1000到+1000 + 1大小的数组,根据优先级,同优先级的进程index相同.每个元素是一个双向链表,这个链表上的所有proc的优先级都相同.这样根据优先级杀进程的时候就会非常方便,要杀指定优先级的进程可以根据优先级获取到一个进程链表,逐个去杀。
static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1];
时序图
lmkd工作流程
入口main
lmkd的入口函数为main(),在lmkd.c
中实现
int main(int argc __unused, char **argv __unused) {
struct sched_param param = {
.sched_priority = 1,
};
/*
* 将此进程现在和未来所使用到的内存锁在物理内存中,防止被交换
*/
if (mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT) && (errno != EINVAL)) {
ALOGW("mlockall failed %s", strerror(errno));
}
/* CAP_NICE required */
//设置调度策略为FIFO
if (sched_setscheduler(0, SCHED_FIFO, ¶m)) {
ALOGW("set SCHED_FIFO failed %s", strerror(errno));
}
if (lmfs_enabled)
start_lmfs();
/*
* 进入死循环等待fd事件
*/
mainloop();
}
init
/*1. 初始化socket监听接口ctrl_sock
*2. 创建epollfd套接字epollfd
*3. 填充epoll_event epev
* 1) epoll监听的事件为EPOLLIN
* 2) 监听的fd为ctrl_sock.sock
* 2) 回调函数为 ctrl_sock.handler_info
*4. 监听事件注册*/
static int init(void) {
struct epoll_event epev;
/*---get _SC_PAGESIZE value---
* PAGE_SIZE is 4096,so total about 16M?
*/
page_k = sysconf(_SC_PAGESIZE);
if (page_k == -1)
page_k = PAGE_SIZE;
page_k /= 1024;
/*创建一个epoll句柄*/
epollfd = epoll_create(MAX_EPOLL_EVENTS);
/*get the lmkd control socket fd to listen the AMS command*/
ctrl_sock.sock = android_get_control_socket("lmkd");
/*list the socket command pass by AMS*/
/* listen():监听来自客户端的tcp socket的连接请求
* #include
* int listen(int sockfd, int backlog)
* 参数sockfd是被listen函数作用的套接字
* 参数backlog是侦听队列的长度。*/
ret = listen(ctrl_sock.sock, MAX_DATA_CONN);
epev.events = EPOLLIN;
ctrl_sock.handler_info.handler = ctrl_connect_handler;
epev.data.ptr = (void *)&(ctrl_sock.handler_info);
/* 注册监听*/
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_sock.sock, &epev) == -1) {
ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno);
return -1;
}
maxevents++;
if (use_inkernel_interface) {
ALOGI("Using in-kernel low memory killer interface");
} else {
/*设置监听/dev/memcg/memory.pressure_level 和 /dev/memcg/cgroup.event_control*/
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;
}
}
return 0;
}
处理socket传递过来的数据
该步主要功能为维护minfree和adj以及process数据链表
在ctrl_connect_handler方法中处理了accept,并开始ctrl_data_handler中读取数据并进行处理:
static void ctrl_command_handler(int dsock_idx) {
len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE);
cmd = lmkd_pack_get_cmd(packet);
switch(cmd) {
case LMK_TARGET:
targets = nargs / 2;
if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
goto wronglen;
cmd_target(targets, packet);
break;
case LMK_PROCPRIO:
if (nargs != 3)
goto wronglen;
cmd_procprio(packet);
break;
case LMK_PROCREMOVE:
if (nargs != 1)
goto wronglen;
cmd_procremove(packet);
break;
case LMK_PROCPURGE:
if (nargs != 0)
goto wronglen;
cmd_procpurge();
break;
case LMK_GETKILLCNT:
if (nargs != 2)
goto wronglen;
kill_cnt = cmd_getkillcnt(packet);
len = lmkd_pack_set_getkillcnt_repl(packet, kill_cnt);
if (ctrl_data_write(dsock_idx, (char *)packet, len) != len)
return;
break;
}
在use_inkernel_interface的情况下,做的事情都是很简单的,只是更新一下文件节点。如果不使用kernel interface,就需要lmkd自己维护两个table,在每次更新adj的时候去更新table。 且在初始化的时候也能看到,如果不使用kernel的lowmemorykiller,则需要lmkd自己获取手机内存状态,如果匹配到了minfree中的等级,则需要通过杀掉一些进程释放内存。
杀进程
杀进程主要是通过之前在init_mp_common
中设置的memory.pressure_level
监听回调函数实现mp_event_common
:
static void mp_event_common(int data, uint32_t events __unused) {
/*
* when only LMK enabled, we can pass vmpressure & swap-pressure to
* PerformanceManagerService because-of enable_adaptive_lmk,
* PerformanceManagerService then force-stop apps from the LRU list
* according to current vmpressure & swap-pressure;
*
* when only MEMCG enabled, the vmpressure & swap-pressure should also
* be passed to PerformanceManagerService
*/
if (!use_inkernel_interface) {
enum vmpressure_level level = (enum vmpressure_level)data;
int vmpressure_value = 0;
switch (level) {
case VMPRESS_LEVEL_LOW:
vmpressure_value = 70;
break;
case VMPRESS_LEVEL_MEDIUM:
vmpressure_value = 80;
break;
case VMPRESS_LEVEL_CRITICAL:
vmpressure_value = 90;
break;
default:
break;
}
handle_vmpressure(vmpressure_value);
}
}
经过层层调用 mp_event_common->handle_vmpressure->find_and_kill_process_adj->find_and_kill_process_adj_locked->kill_one_process
参考文档:
- https://source.android.com/devices/tech/perf/lmkd
- https://www.sohu.com/a/238012686_467784
- http://gityuan.com/2016/09/17/android-lowmemorykiller/
- https://blog.csdn.net/u011733869/article/details/78820240
?