lowmemorykiller
Android系统中当Activity切换到后台,activity所在进程并不会马上退出,还是会继续存在系统中,便于再次启动提高响应速度(热启动),当内存达到一定极限值,lmkd会根据策略来杀掉一些优先级较低的进程来保障系统正常运行。lowmemorykiller Android8.0之前主要实现在kernel中,8.0以后主要在lmkd中,kernel使用vmpressure机制,达到和kernel解耦的目的, Android10.0在Android8.0的基础上新增了几个命令, lmkd也复杂一些了
AMS中部分更新系统adj值函数
bindServiceLocked
unbindServiceLocked
realStartServiceLocked
sendServiceArgsLocked
bringDownServiceLocked
bringDownServiceIfNeededLocked
serviceDoneExecutingLocked
resumeTopActivityInnerLocked
finishSubActivityLocked
updateProcessForegroundLocked
appDiedLocked
killAllBackgroundProcesses
attachApplicationLocked
getContentProviderImpl
removeContentProvider
publishContentProviders
processCurBroadcastLocked
deliverToRegisteredReceiverLocked
processNextBroadcast
AMS和lmkd之间的关系图
命令对应关系
命令 | ProcessList | lmkd |
---|---|---|
LMK_TARGET | updateOomLevels | cmd_target |
LMK_PROCPRIO | setOomAdj | cmd_procprio |
LMK_PROCREMOVE | remove | cmd_procremove |
LMK_GETKILLCNT | getLmkdKillCount | cmd_procpurge |
LMK_PROCPURGE | writeLmkd | cmd_procpurge |
Android Framework中的具体实现在ProcessList中
ProcessList.ava
// 通信中的命令
// LMK_TARGET <minfree> <minkillprio> ... (up to 6 pairs)
// LMK_PROCPRIO <pid> <uid> <prio>
// LMK_PROCREMOVE <pid>
// LMK_PROCPURGE
// LMK_GETKILLCNT
// LMK_FASTKILL
// 更新mOomMinFree,mOomAdj的值到 lmkd
static final byte LMK_TARGET = 0;
// 设置adj
static final byte LMK_PROCPRIO = 1;
// 移除进程
static final byte LMK_PROCREMOVE = 2;
// 更新adj值
static final byte LMK_PROCPURGE = 3;
// 获取kill进程的次数
static final byte LMK_GETKILLCNT = 4;
// 快速杀死进程
static final byte LMK_FASTKILL = 5;
// /proc/pid/oom_adj:代表当前进程的优先级
// /proc/pid/oom_score_adj:上层优先级,跟ProcessList中的优先级对应
// updateOomLevels将mOomMinFree计算好后通过LMK_TARGET指令发送给 lkmd 进程
private final int[] mOomAdj = new int[] {
FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
PERCEPTIBLE_LOW_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_LMK_FIRST_ADJ
};
// These are the low-end OOM level limits. This is appropriate for an
// HVGA or smaller phone with less than 512MB. Values are in KB.
private final int[] mOomMinFreeLow = new int[] {
12288, 18432, 24576,
36864, 43008, 49152
};
// These are the high-end OOM level limits. This is appropriate for a
// 1280x800 or larger screen with around 1GB RAM. Values are in KB.
private final int[] mOomMinFreeHigh = new int[] {
73728, 92160, 110592,
129024, 147456, 184320
};
// The actual OOM killer memory levels we are using.
private final int[] mOomMinFree = new int[mOomAdj.length];
updateOomLevels
private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
// 计算mOomMinFree的值,具体细节忽略了
for (int i=0; i<mOomAdj.length; i++) {
int low = mOomMinFreeLow[i];
int high = mOomMinFreeHigh[i];
if (is64bit) {
// Increase the high min-free levels for cached processes for 64-bit
if (i == 4) high = (high*3)/2;
else if (i == 5) high = (high*7)/4;
}
mOomMinFree[i] = (int)(low + ((high-low)*scale));
}
if (write) {
// 将计算出的mOomMinFree值更新到lmkd中,显示相关信息变化会调用updateOomLevels函数
ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));
buf.putInt(LMK_TARGET);
for (int i=0; i<mOomAdj.length; i++) {
buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);
buf.putInt(mOomAdj[i]);
}
writeLmkd(buf);
SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));
}
}
setOomAdj
public static final void setOomAdj(int pid, int uid, int amt) {
if (amt == UNKNOWN_ADJ)
return;
long start = SystemClock.elapsedRealtime();
ByteBuffer buf = ByteBuffer.allocate(4 * 4);
buf.putInt(LMK_PROCPRIO);
buf.putInt(pid);
buf.putInt(uid);
buf.putInt(amt);
writeLmkd(buf);
long now = SystemClock.elapsedRealtime();
if ((now-start) > 250) {
Slog.w("ActivityManager", "SLOW OOM ADJ: " + (now-start) + "ms for pid " + pid
+ " = " + amt);
}
}
remove
public static final void remove(int pid) {
// 把对应pid进程从lkmd中移除
ByteBuffer buf = ByteBuffer.allocate(4 * 2);
buf.putInt(LMK_PROCREMOVE);
buf.putInt(pid);
writeLmkd(buf);
}
lmkd代码分析
// 双向链表结构体
struct adjslot_list {
struct adjslot_list *next;
struct adjslot_list *prev;
};
// 记录进程相关信息结构体
struct proc {
struct adjslot_list asl;
int pid;
uid_t uid;
int oomadj;
struct proc *pidhash_next;
};
#define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN)
// Android系统中启动的apk进程信息,以adj为key, 保存在procadjslot_list中
static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1];
main
int main(int argc __unused, char **argv __unused) {
struct sched_param param = {
.sched_priority = 1,
};
mlockall(MCL_FUTURE);
// 设置进程调度策略
sched_setscheduler(0, SCHED_FIFO, ¶m);
// 初始化
if (!init())
mainloop();
ALOGI("exiting");
return 0;
}
init
static int init(void) {
struct epoll_event epev;
int i;
int ret;
// 创建epoll监听文件句柄
epollfd = epoll_create(MAX_EPOLL_EVENTS);
if (epollfd == -1) {
ALOGE("epoll_create failed (errno=%d)", errno);
return -1;
}
// 获取lmkd socket 文件句柄
ctrl_lfd = android_get_control_socket("lmkd");
if (ctrl_lfd < 0) {
ALOGE("get lmkd control socket failed");
return -1;
}
// 监听lmkd socket
ret = listen(ctrl_lfd, 1);
if (ret < 0) {
ALOGE("lmkd control socket listen failed (errno=%d)", errno);
return -1;
}
// Socket监听函数注册,ctrl_connect_handler处理AnroidFramework层发送过来命令
epev.events = EPOLLIN;
epev.data.ptr = (void *)ctrl_connect_handler;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_lfd, &epev) == -1) {
ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno);
return -1;
}
maxevents++;
// 是否使用内核接口,Android8.0以后基本上不使用kernel接口了
// 注册vmpressure机制函数来监听系统内存状况来处理杀进程
use_inkernel_interface = !access(INKERNEL_MINFREE_PATH, W_OK);
if (use_inkernel_interface) {
ALOGI("Using in-kernel low memory killer interface");
} else {
// vmpressure相关初始化 mp_event函数处理vmpressure事件相关处理
ret = init_mp(MEMPRESSURE_WATCH_LEVEL, (void *)&mp_event);
}
// 初始化进程链表
for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
procadjslot_list[i].next = &procadjslot_list[i];
procadjslot_list[i].prev = &procadjslot_list[i];
}
return 0;
}
mainloop
static void mainloop(void) {
while (1) {
struct epoll_event events[maxevents];
int nevents;
int i;
ctrl_dfd_reopened = 0;
// 等待事件到来
nevents = epoll_wait(epollfd, events, maxevents, -1);
if (nevents == -1) {
if (errno == EINTR)
continue;
ALOGE("epoll_wait failed (errno=%d)", errno);
continue;
}
for (i = 0; i < nevents; ++i) {
if (events[i].events & EPOLLERR)
ALOGD("EPOLLERR on event #%d", i);
// 处理事件
if (events[i].data.ptr)
(*(void (*)(uint32_t))events[i].data.ptr)(events[i].events);
}
}
}
lmkd中处理AMS发送过来的数据
static void ctrl_command_handler(void) {
int ibuf[CTRL_PACKET_MAX / sizeof(int)];
int len;
int cmd = -1;
int nargs;
int targets;
// 检测长度
len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX);
if (len <= 0)
return;
nargs = len / sizeof(int) - 1;
if (nargs < 0)
goto wronglen;
// 大小端处理
cmd = ntohl(ibuf[0]);
switch(cmd) {
// // 更新mOomMinFree,mOomAdj的值到 lmkd
case LMK_TARGET:
targets = nargs / 2;
if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
goto wronglen;
cmd_target(targets, &ibuf[1]);
break;
// 设置adj
case LMK_PROCPRIO:
if (nargs != 3)
goto wronglen;
cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3]));
break;
// 移除进程
case LMK_PROCREMOVE:
if (nargs != 1)
goto wronglen;
cmd_procremove(ntohl(ibuf[1]));
break;
default:
ALOGE("Received unknown command code %d", cmd);
return;
}
return;
init_mp
static int init_mp(char *levelstr, void *event_handler)
{
int mpfd;
int evfd;
int evctlfd;
char buf[256];
struct epoll_event epev;
int ret;
// 监听vmpressure机制事件
epev.events = EPOLLIN;
epev.data.ptr = event_handler;
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evfd, &epev);
if (ret == -1) {
ALOGE("epoll_ctl for level %s failed; errno=%d", levelstr, errno);
goto err;
}
maxevents++;
mpevfd = evfd;
return 0;
}
mp_event
static void mp_event(uint32_t events __unused) {
int ret;
unsigned long long evcount;
struct sysmeminfo mi;
int other_free;
int other_file;
int killed_size;
bool first = true;
ret = read(mpevfd, &evcount, sizeof(evcount));
if (ret < 0)
ALOGE("Error reading memory pressure event fd; errno=%d",
errno);
if (time(NULL) - kill_lasttime < KILL_TIMEOUT)
return;
while (zoneinfo_parse(&mi) < 0) {
// Failed to read /proc/zoneinfo, assume ENOMEM and kill something
// 杀进程
find_and_kill_process(0, 0, true);
}
other_free = mi.nr_free_pages - mi.totalreserve_pages;
other_file = mi.nr_file_pages - mi.nr_shmem;
do {
// 杀进程
killed_size = find_and_kill_process(other_free, other_file, first);
if (killed_size > 0) {
first = false;
other_free += killed_size;
other_file += killed_size;
}
} while (killed_size > 0);
}
find_and_kill_process
static int find_and_kill_process(int other_free, int other_file, bool first)
{
int i;
int min_score_adj = OOM_SCORE_ADJ_MAX + 1;
int minfree = 0;
int killed_size = 0;
for (i = 0; i < lowmem_targets_size; i++) {
minfree = lowmem_minfree[i];
if (other_free < minfree && other_file < minfree) {
// 找到最小的min_score_adj
min_score_adj = lowmem_adj[i];
break;
}
}
if (min_score_adj == OOM_SCORE_ADJ_MAX + 1)
return 0;
for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
struct proc *procp;
retry:
// 从lkmd的procadjslot_list链表中找到对应adj值的进程链表
procp = proc_adj_lru(i);
if (procp) {
// 如果进程存在,则将进程杀掉
killed_size = kill_one_process(procp, other_free, other_file,
minfree, min_score_adj, first);
if (killed_size < 0) {
goto retry;
} else {
return killed_size;
}
}
}
return 0;
}
kill_one_process
static int kill_one_process(struct proc *procp, int other_free, int other_file,
int minfree, int min_score_adj, bool first)
{
int pid = procp->pid;
uid_t uid = procp->uid;
char *taskname;
int tasksize;
int r;
// 获取进程对应的名字
taskname = proc_get_name(pid);
if (!taskname) {
pid_remove(pid);
return -1;
}
// 获取进程所占内存大小
tasksize = proc_get_size(pid);
if (tasksize <= 0) {
pid_remove(pid);
return -1;
}
// 杀进程
r = kill(pid, SIGKILL);
// 杀进程组
killProcessGroup(uid, pid, SIGKILL);
// 将对应进程信息从lkmd中移除
pid_remove(pid);
if (r) {
ALOGE("kill(%d): errno=%d", procp->pid, errno);
return -1;
} else {
return tasksize;
}
}
总结
Android Framework中和lmkd通过Socket来通信,主要功能如下:
1 更新lowmemorykiller中adj和杀进程的内存等级值。
2 更新pid的list中对应的adj。
3 移除已经被杀掉的app进程。
4 处理kernel发送过来的vmpressure事件