这里看3个问题:
1. AMS 如何创建新进程?
2. AMS如何更新LRU?
3. AMS如何更新OOM_ADJ?
AMS对于进程的管理主要体现在两个方面:
第一是动态调整进程再mLruProcess中的位置,
第二就是调整进程的oom_adj的值,
这两项都和系统的内存自动回收有关系,当系统的内存不足时,
系统主要根据oom_adj的值来选择杀死一些进程以释放内存,这个值越大表示进程越容易被杀死。
AMS如何启动一个进程?
AMS是调用addAppLocked方法来启动一个进程的,这个方法实现如下:
final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
String abiOverride) {
ProcessRecord app;
//isolated 表示当前需要启动的App, 是不是一个独立的进程?
if (!isolated) { //如果不是独立进程,就从正在运行的进程列表中找
app = getProcessRecordLocked(info.processName, info.uid, true);
} else { //如果是独立进程,则重现创建一个ProcessRecord
app = null;
}
//这是一个独立的进程,或者在 正在运行的进程列表中没有找到记录
if (app == null) {
//创建一个新的ProcessReocrd对象
app = newProcessRecordLocked(info, null, isolated, 0);
//更新LRU列表和oom_adj值,后面具体分析
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
// This package really, really can not be stopped.
// 将这个App的包设置为启动状态,这样这个App就可以接受隐式Intent了
try {
AppGlobals.getPackageManager().setPackageStoppedState(
info.packageName, false, UserHandle.getUserId(app.uid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ info.packageName + ": " + e);
}
//如果这个app带有PERSISTENT的话,将相应App打上标记
if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
app.persistent = true;
app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
//如果App的主线程为空的话,
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
//启动实际App
startProcessLocked(app, "added application", app.processName, abiOverride,
null /* entryPoint */, null /* entryPointArgs */);
}
return app;
}
addAppLocked方法会根据参数isolated来决定这个进程是不是一个独立的进程,如果是那就创建一个新的ProcessRecord对象,如果不是的话,那就调用getProcessRecordLocked方法在当前运行的进程列表中查找进程,我们看下这个方法的定义:
final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) {
//如果是一个系统级别的UID,也就是说这个App是由system用户启动的
if (uid == Process.SYSTEM_UID) {
// The system gets to run in any process. If there are multiple
// processes with the same uid, just pick the first (this
// should never happen).
// 如果是系统uid,就直接取第一个
SparseArray<ProcessRecord> procs = mProcessNames.getMap().get(processName);
if (procs == null) return null;
final int procCount = procs.size();
for (int i = 0; i < procCount; i++) {
final int procUid = procs.keyAt(i);
if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) {
// Don't use an app process or different user process for system component.
continue;
}
return procs.valueAt(i);
}
}
//如果不是系统UID的话,首先从mProcessNames中查找正在运行的进程记录
ProcessRecord proc = mProcessNames.get(processName, uid);
if (false && proc != null && !keepIfLarge //这个分支走不到,测试分支
&& proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
&& proc.lastCachedPss >= 4000) {
// Turn this condition on to cause killing to happen regularly, for testing.
if (proc.baseProcessTracker != null) {
proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
}
proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
} else if (proc != null && !keepIfLarge //走这个分支,如果传递过来的KeepIfLarge是true, 也不会走这个分支
&& mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
&& proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc.lastCachedPss);
if (proc.lastCachedPss >= mProcessList.getCachedRestoreThresholdKb()) {
if (proc.baseProcessTracker != null) {
proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
}
proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
}
}
//直接返回
return proc;
}
getProcessRecordLocked方法的逻辑是比较简单的,他分为两部分:uid是system uid和uid是普通uid两种情况。具体的逻辑上面的注释已经解释,这里就不赘述。需要补充的是,上面如果我们的uid是system uid的话那么会使用UserHandle.isApp来判断这是不是一个app进程,他的实现如下:
frameworks\base\core\java\android\os\userhandle.java:
public static boolean isApp(int uid) {
if (uid > 0) {
final int appId = getAppId(uid);
return appId >= Process.FIRST_APPLICATION_UID && appId <= Process.LAST_APPLICATION_UID;
} else {
return false;
}
}
这里的逻辑很简单,主要就是根据uid获得app的id,然后如果app id是在Process.FIRST_APPLICATION_UID和Process.LAST_APPLICATION_UID之间(这是正常app id应该处于的范围)的话,那就是一个合理的app进程。
现在我们回到addAppLocked方法,我们刚才分析了如果不是独立进程的情况的逻辑,总结来说就是通过getProcessRecordLocked查找当前系统中正在运行的进程记录,并且把这个记录保存下来。现在我们看一下如果请求的是一个独立的进程的话(这也是最常见的情形),处理的方式是什么样的.
新建了一个ProcessRecord对象之后的操作就是最重要的操作了:
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
这两步操作设计AMS管理进程的核心工作, 放后面分析。
我们稍后详细分析,我们先接下来的逻辑,接下来的逻辑中最重要的就是调用startProcessLocked方法实际启动一个进程:
mPersistentStartingProcesses.add(app);
startProcessLocked(app, "added application", app.processName, abiOverride,
null /* entryPoint */, null /* entryPointArgs */);
在AMS中startProcessLocked方法实现了多态,但是根据这里的参数我们可以确定我们调用的方法是哪一个。startProcessLocked方法比较长,这里我们分部来分析:
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
long startTime = SystemClock.elapsedRealtime(); //首先需要记录一下app的启动时间
if (app.pid > 0 && app.pid != MY_PID) {
checkTime(startTime, "startProcess: removing from pids map"); //这个startTime是给checkTime方法使用的
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.remove(app.pid);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
}
checkTime(startTime, "startProcess: done removing from pids map");
app.setPid(0);
}
首先需要记录一下app的启动时间,这个数据是给checkTime方法使用的,checkTime方法的定义如下:
[email protected]
private void checkTime(long startTime, String where) {
long now = SystemClock.uptimeMillis();
if ((now-startTime) > 50) {
// If we are taking more than 50ms, log about it.
Slog.w(TAG, "Slow operation: " + (now-startTime) + "ms so far, now at " + where);
}
}
我们看到,checkTime的逻辑很简单,主要就是看看当前的时间和传递进来的时间是不是相差50ms,如果相差50ms的话那就需要打下log,这部分的逻辑就是这样。
我们继续startProcessLocked的分析:
if (app.pid > 0 && app.pid != MY_PID) {
checkTime(startTime, "startProcess: removing from pids map");
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.remove(app.pid);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
}
checkTime(startTime, "startProcess: done removing from pids map");
app.setPid(0);
}
再获得启动开始时间之后,当app的pid大于0并且pid不是本进程PID的话,就要把当前pid的进程从mPidsSelfLocked列表中移除,防止重复,因为后面我们还要加入;
然后就是移除PROC_START_TIMEOUT_MSG消息,这个消息是AMS用来控制app启动时间的,如果启动超时了就发出效果消息,下面我们会设置这个消息,现在需要取消之前设置的消息,防止干扰。
接下来的逻辑:
mProcessesOnHold.remove(app);
这里是将app进程从mProcessesOnHold列表中清除,这个列表是什么呢?这个列表是系统中在AMS没有启动之前请求启动的app,这些app当时没有启动,被hold了,当AMS启动完成的时候需要将他们启动;现在如果我们启动的app就在这个列表中的, 那么自然需要将它移除,防止重复启动。我们可以从mProcessesOnHold的定义的地方看到这一点:
/**
* List of records for processes that someone had tried to start before the
* system was ready. We don't start them at that point, but ensure they
* are started by the time booting is complete.
*/
final ArrayList<ProcessRecord> mProcessesOnHold = new ArrayList<ProcessRecord>();
接下来就要更新cpu使用统计信息:
checkTime(startTime, "startProcess: starting to update cpu stats");
updateCpuStats();
checkTime(startTime, "startProcess: done updating cpu stats");
cpu统计使用信息也是AMS的一个重要的任务,这个任务就是通过启动一个独立的线程去获得cpu的使用情况,我们看一下updateCpuStats的实现:
void updateCpuStats() {
final long now = SystemClock.uptimeMillis();
if (mLastCpuTime.get() >= now - MONITOR_CPU_MIN_TIME) { //5s采样一次
return;
}
if (mProcessCpuMutexFree.compareAndSet(true, false)) {
synchronized (mProcessCpuThread) {
mProcessCpuThread.notify();
}
}
}
我们发现它的逻辑很简单,首先是判断当前时间是不是和上次检查时间相差MONITOR_CPU_MIN_TIME(5s)以上,如果是就继续,如果不是就返回,因为不能过于频繁做这件事情,它比较耗电。
工作的方式就是唤醒mProcessCpuThread去采集cpu的信息。这个线程的实现我们这里先不分析,这块和我们的进程管理相关不是很密切,我后面的文章会详细分析这块的内容。
在startProcessLocked的接下来的逻辑中,主要就是app启动的一些参数设置,条件检查等操作,这里我们直接略过,我们直接看实际进程启动的部分:
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
boolean isActivityProcess = (entryPoint == null);
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
app.processName);
checkTime(startTime, "startProcess: asking zygote to start proc");
Process.ProcessStartResult startResult = Process.start(entryPoint, //实际启动进程
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
checkTime(startTime, "startProcess: returned from zygote!");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
上面代码中最核心的代码就是调用Process.start静态方法部分,这个调用会通过socket和zygote建立链接请求,fork新进程,我们看一下start方法实现:
frameworks\base\core\java\android\os\process.java
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
里的startViaZygote方法会和zygote建立链接并