一:背景
系统资源是有限的,后台应用如果长时间使用CPU,可能会影响前台应用,造成前台应用卡顿(抢占CPU资源)。如何合理的分配系统资源、限制后台应用、把资源留给前台应用是个必须解决的事情。最近梳理代码,发现Google做了一部分这个事情,在此记录一下。
二:功能实现
2.1 ActivityManagerService.finishBooting
系统开机完成,ActivitymanagerService.finishBooting函数会被调用(具体调用逻辑这里就不详细列出来了,有兴趣的可以跟下调用栈),在finishBooting会发送CHECK_EXCESSIVE_POWER_USE_MSG message来开始监听之旅。在接收到该message消息时,会检查应用的CPU使用情况,并以5分钟的时间间隔来不断循环检查。
final void finishBooting() {
...
synchronized (this) {
...
// Start looking for apps that are abusing wake locks.
Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG);
mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL);
...
}
}
final class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
...
case CHECK_EXCESSIVE_POWER_USE_MSG: {
//检查应用的CPU使用情况
checkExcessivePowerUsage();
removeMessages(CHECK_EXCESSIVE_POWER_USE_MSG);
Message nmsg = obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG);
//以POWER_CHECK_INTERVAL时间间隔轮询
sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL);
} break;
...
}
}
2.2 ActivityManagerService.checkExcessivePowerUsage
遍历LRU list所有进程,针对优先级低于PROCESS_STATE_HOME的进程,根据进程状态改变距离现在的时间,设置不同的限制阈值,然后调用updateAppProcessCpuTimeLPr继续检查流程。
private void checkExcessivePowerUsage() {
//更新CPU统计信息
updateCpuStatsNow();
...
synchronized (mProcLock) {
//mLastPowerCheckUptime为上一次检查时间,第一次检查时,该值为0
final boolean doCpuKills = mLastPowerCheckUptime != 0;
final long curUptime = SystemClock.uptimeMillis();
//从上次检查到现在的时间
final long uptimeSince = curUptime - mLastPowerCheckUptime;
//把当前时间赋值给mLastPowerCheckUptime
mLastPowerCheckUptime = curUptime;
//遍历LRU list中所有进程
mProcessList.forEachLruProcessesLOSP(false, app -> {
//如果是空进程,直接return
if (app.getThread() == null) {
return;
}
//优先级低于PROCESS_STATE_HOME的进程
if (app.mState.getSetProcState() >= ActivityManager.PROCESS_STATE_HOME) {
//检查阈值
int cpuLimit;
//进程开始变成非重要进程到现在的时间
long checkDur = curUptime - app.mState.getWhenUnimportant();
//根据进程状态改变的时间,设置不同的阈值
if (checkDur <= mConstants.POWER_CHECK_INTERVAL) {//5min
cpuLimit = mConstants.POWER_CHECK_MAX_CPU_1;//25
} else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 2)
|| app.mState.getSetProcState() <= ActivityManager.PROCESS_STATE_HOME) {//10min
cpuLimit = mConstants.POWER_CHECK_MAX_CPU_2;//25
} else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 3)) {//15min
cpuLimit = mConstants.POWER_CHECK_MAX_CPU_3;//10
} else {
cpuLimit = mConstants.POWER_CHECK_MAX_CPU_4;//2
}
//继续检查流程
updateAppProcessCpuTimeLPr(uptimeSince, doCpuKills, checkDur, cpuLimit, app);
...
}
});
}
}
2.3 ActivityManagerService.updateAppProcessCpuTimeLPr
通过ProcessProfileRecord,计算出进程已经使用CPU的时间,调用checkExcessivePowerUsageLPr函数来检查是否超过限制阈值,如果超过则杀进程
private void updateAppProcessCpuTimeLPr(final long uptimeSince, final boolean doCpuKills,
final long checkDur, final int cpuLimit, final ProcessRecord app) {
synchronized (mAppProfiler.mProfilerLock) {
final ProcessProfileRecord profile = app.mProfile;
//通过ProcessProfileRecord获取当前CPU使用时间
final long curCpuTime = profile.mCurCpuTime.get();
//通过ProcessProfileRecord获取上次CPU使用时间
final long lastCpuTime = profile.mLastCpuTime.get();
if (lastCpuTime > 0) {
//该进程已经使用CPU的时间
final long cpuTimeUsed = curCpuTime - lastCpuTime;
//检查进程CPU使用时间是否超过阈值
if (checkExcessivePowerUsageLPr(uptimeSince, doCpuKills, cpuTimeUsed,
app.processName, app.toShortString(), cpuLimit, app)) {
mHandler.post(() -> {
synchronized (ActivityManagerService.this) {
//如果是空进程或者优先级高于PROCESS_STATE_HOME的进程
if (app.getThread() == null
|| app.mState.getSetProcState() < ActivityManager.PROCESS_STATE_HOME) {
return;
}
//杀进程
app.killLocked("excessive cpu " + cpuTimeUsed + " during "
+ uptimeSince + " dur=" + checkDur + " limit=" + cpuLimit,
ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU,
true);
}
});
profile.reportExcessiveCpu();
}
}
profile.mLastCpuTime.set(curCpuTime);
}
}
2.4 ActivityManagerService.checkExcessivePowerUsageLPr
检查CPU使用时间除以距离上次检查的时间的百分比是否超过阈值
private boolean checkExcessivePowerUsageLPr(final long uptimeSince, boolean doCpuKills,
final long cputimeUsed, final String processName, final String description,
final int cpuLimit, final ProcessRecord app) {
...
// If the process has used too much CPU over the last duration, the
// user probably doesn't want this, so kill!
if (doCpuKills && uptimeSince > 0) {
//如果CPU使用时间超过阈值(CPU使用时间除以距离上次检查的时间,cpuLimit可以看做百分比)
if (((cputimeUsed * 100) / uptimeSince) >= cpuLimit) {
mBatteryStatsService.reportExcessiveCpu(app.info.uid, app.processName,
uptimeSince, cputimeUsed);
app.getPkgList().forEachPackageProcessStats(holder -> {
final ProcessState state = holder.state;
FrameworkStatsLog.write(
FrameworkStatsLog.EXCESSIVE_CPU_USAGE_REPORTED,
app.info.uid,
processName,
state != null ? state.getPackage() : app.info.packageName,
holder.appVersion);
});
return true;
}
}
return false;
}
三:总结
1 该功能只针对优先级低于PROCESS_STATE_HOME的非空进程做限制,对其他进程没有任何限制。
2. 进程越早变成低优先级进程(优先级低于PROCESS_STATE_HOME),限制阈值就越小,也就是进程越容易被杀。
3. 限制措施是kill进程,这种查杀方式在进程被杀后,允许进程通过service等手段自启。