一:背景
出现查杀:
Line 11157: 08-13 11:17:02.407361 1697 1731 I am_kill : [0,18313,com.ss.android.ugc.aweme,900,excessive cpu 72610 during 60054 dur=107220 limit=75] //在60s的检查周期内,进程使用了72s(变成非重要进程的时间),超过了阈值75(时间越久值越大)
二:方案介绍
系统资源是有限的,后台应用如果长时间使用CPU,可能会影响前台应用,造成前台应用卡顿(抢占CPU资源)。轮询查看后台高负载的应用,发现其CPU占用高可通知进行查杀。
三:功能实现
1 ActivityManagerService.finishBooting
系统开机完成,ActivitymanagerService.finishBooting函数会被调用,在finishBooting(5分钟后)会发送CHECK_EXCESSIVE_POWER_USE_MSG message来开始监听之旅。在接收到该message消息时,会检查应用的CPU使用情况,并以5分钟的时间间隔来不断循环检查。
Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG);
mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL);
case CHECK_EXCESSIVE_POWER_USE_MSG: {
checkExcessivePowerUsage();
removeMessages(CHECK_EXCESSIVE_POWER_USE_MSG);
Message nmsg = obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG);
sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL);
private static final long DEFAULT_POWER_CHECK_INTERVAL = (DEBUG_POWER_QUICK ? 1 : 5) * 60*1000;
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);
...
}
});
}
}
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);
}
}
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.轮询机制修改太短是否会更加消耗资源,适得其反。(高手课中好像有讲相关的设计)
2.初步可以修改参数
173 private static final long DEFAULT_POWER_CHECK_INTERVAL = ((DEBUG_POWER_QUICK ||XXX)) ? 1 : 5) * 60*1000;
174 private static final int DEFAULT_POWER_CHECK_MAX_CPU_1 = XXX ? 25*3 : 25;
175 private static final int DEFAULT_POWER_CHECK_MAX_CPU_2 = XXX ? 25*3 : 25;
176 private static final int DEFAULT_POWER_CHECK_MAX_CPU_3 = XXX? 10*3 : 10;
177 private static final int DEFAULT_POWER_CHECK_MAX_CPU_4 = XXX? 2*3 : 2;
但更多的是要理解进程何时变为非重要进程。
3.在修改参数的基础上,针对重要应用,是否可以定制cpu使用量,既保证整机性能又满足用户使用。
参考: