lowmemorykiller

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之间的关系图

在这里插入图片描述

命令对应关系
命令ProcessListlmkd
LMK_TARGETupdateOomLevelscmd_target
LMK_PROCPRIOsetOomAdjcmd_procprio
LMK_PROCREMOVEremovecmd_procremove
LMK_GETKILLCNTgetLmkdKillCountcmd_procpurge
LMK_PROCPURGEwriteLmkdcmd_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, &param);
	 // 初始化
    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事件

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值