Android ActivityManagerService(AMS)的进程管理
Posted createchance
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android ActivityManagerService(AMS)的进程管理相关的知识,希望对你有一定的参考价值。
android中的AMS的职责是比较多的,其中一个比较重要的职责就是app进程的管理,比如我们调用startActivity方法启动一个activity的时候,可能对应的那个进程没有启动,因此需要启动那个进程,而且对于这个进程还要有一些必要的管理过程,比如将它放到LRU(least recently used)列表中去等。本文就AMS的进程管理基本逻辑和过程做一个简要的分析,以帮助大家弄清楚AMS的进程管理。
Android的应用进程是什么?
这里我们讲的是android的应用进程,并不是native层的进程,这里需要知道。在android中,进程(Process)的概念是被弱化的,我们知道在传统的系统中进程是静态程序执行的载体,程序的代码段,数据等信息全部都是在进程的管理之中的,而且进程中的多个组件都是在一个进程中的,并且他们的生命周期都是和进程息息相关的。但是在android中,进程只不过是一系列运行组件的容器而已,这些组件可以运行在不同的进程之中,也就是说某个app中的某个组件可以运行在本地进程之中,也可以运行在另外一个进程中;说白了,也就是说android的app中的组件只是一个静态的程序级别的概念,真正运行的时候这些组件可能“各立山头”,互补相关;要做到这一点也很容易,只要在AndroidManifest中的相应组件指定android:process属性就可以了。同时,不同的app中的不同组件也可以运行在一个进程中,可以通过在AndroidManifest指定相应的进程名称就可以了。
虽然在android的开发中,不再强调进程的概念,但是进程毕竟是实际存在于android系统中,只是它和我们认识到的传统进程不太一样,所以我们的AMS还是需要对进程进行管理的。AMS对于进程的管理主要体现在两个方面:第一是动态调整进程再mLruProcess中的位置,第二就是调整进程的oom_adj的值,这两项都和系统的内存自动回收有关系,当系统的内存不足时,系统主要根据oom_adj的值来选择杀死一些进程以释放内存,这个值越大表示进程越容易被杀死。
AMS启动进程流程
在Android ActivityManagerService(AMS)的启动分析一文中我们提到了AMS是调用addAppLocked方法来启动一个进程的,这个方法实现如下:
final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
String abiOverride)
ProcessRecord app;
// isolated表示当前需要启动的app是不是一个独立的进程,如果是独立的话那就重新
// 创建一个ProcessRecord,如果不是的话那就从正在运行的进程列表中找。
if (!isolated)
app = getProcessRecordLocked(info.processName, info.uid, true);
else
app = null;
// 这是一个独立的进程或者在正在运行的列表中没有找到相应的记录
if (app == null)
// 新建一个ProcessRecord对象
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;
// 这个值下面我们分析oom_adj中会说明
app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
// 如果app中的thread(就是主线程)为空的话
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方法在当前运行的进程列表中查找进程,我们看下这个方法的定义:
getProcessRecordLocked@ActivityManagerService.java
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).
// 上面英文注释说的很明白,如果有多个system 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);
// 如果这是一个合理的app进程,或者uid不是要求的uid的话,那就跳过。
// 原因下面的英文注释说的很清楚。
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);
// 一般会走这个分支,但是由于我们的addAppLocked传递进来的keepIfLarge是true,因此这个分支也不会走。
else if (proc != null && !keepIfLarge
&& 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进程,他的实现如下:
isApp@UserHandle.java
/** @hide */
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查找当前系统中正在运行的进程记录,并且把这个记录保存下来。现在我们看一下如果请求的是一个独立的进程的话(这也是最常见的情形),处理的方式是什么样的:
addAppLocked@ActivityManagerService.java
final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
String abiOverride)
ProcessRecord app;
if (!isolated)
app = getProcessRecordLocked(info.processName, info.uid, true);
else
app = null;
if (app == null)
app = newProcessRecordLocked(info, null, isolated, 0);
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
我们看到,如果是一个独立的进程的话,那么首先app肯定就直接赋值为null,接下来我们会调用newProcessRecordLocked新建一个ProcessRecord对象,这个方法的具体处理因为和我们AMS的进程管理不是特别相关,我们就不分析了,感兴趣的读者可以自行分析。新建了一个ProcessRecord对象之后的操作就是最重要的操作了:
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
这两步操作设计AMS管理进程的核心工作,我们稍后详细分析,我们先接下来的逻辑,接下来的逻辑中最重要的就是调用startProcessLocked方法实际启动一个进程:
startProcessLocked(app, "added application", app.processName, abiOverride,
null /* entryPoint */, null /* entryPointArgs */);
在AMS中startProcessLocked方法实现了多态,但是根据这里的参数我们可以确定我们调用的方法是哪一个。startProcessLocked方法比较长,这里我们分部来分析:
startProcessLocked@ActivityManagerService.java
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs)
long startTime = SystemClock.elapsedRealtime();
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的启动时间,这个数据是给checkTime方法使用的,checkTime方法的定义如下:
checkTime@ActivityManagerService.java
private void checkTime(long startTime, String where)
long now = SystemClock.elapsedRealtime();
if ((now-startTime) > 1000)
// If we are taking more than a second, log about it.
Slog.w(TAG, "Slow operation: " + (now-startTime) + "ms so far, now at " + where);
我们看到,checkTime的逻辑很简单,主要就是看看当前的时间和传递进来的时间是不是相差1000ms,如果相差1s的话那就需要打下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使用统计信息:
updateCpuStats();
cpu统计使用信息也是AMS的一个重要的任务,这个任务就是通过启动一个独立的线程去获得cpu的使用情况,我们看一下updateCpuStats的实现:
void updateCpuStats()
final long now = SystemClock.uptimeMillis();
if (mLastCpuTime.get() >= now - MONITOR_CPU_MIN_TIME)
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方法实现:
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建立链接并且创建进程,这里我们就不详细分析zygote部分的fork逻辑了。我们再次回到startProcessLocked方法中分析剩下的逻辑,刚才我们上面的请求zygote的部分是一个异步请求,会立即返回,当时实际进程启动需要多长的时间是不确定的,但是我们不能无限制等待,需要有一个启动超时机制,在startProcessLocked接下来的逻辑中就启动了一个延迟消息:
synchronized (mPidsSelfLocked)
this.mPidsSelfLocked.put(startResult.pid, app);
if (isActivityProcess)
Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg, startResult.usingWrapper
? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
这个消息是PROC_START_TIMEOUT_MSG用于通知应用启动超时了,由mHandler负责处理。并且超时的阀值是根据当前启动的进程是不是一个wrapper进程来决定的,如果是wrapper进程的话那超时就是PROC_START_TIMEOUT_WITH_WRAPPER(1200s),如果不是则PROC_START_TIMEOUT(10s),正常情况下都是后者,wrapper进程使用场景较少。为了弄清楚假如发生了启动超时,系统怎么处理,我们还需要看一下mHandler对这个消息的处理:
mHandler@ActivityManagerService.java
case PROC_START_TIMEOUT_MSG:
if (mDidDexOpt)
mDidDexOpt = false;
Message nmsg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
nmsg.obj = msg.obj;
mHandler.sendMessageDelayed(nmsg, PROC_START_TIMEOUT);
return;
ProcessRecord app = (ProcessRecord)msg.obj;
synchronized (ActivityManagerService.this)
processStartTimedOutLocked(app);
break;
首先需要判断当前这个app是不处在dex优化过程中,如果是处在dex优化过程中的话,那么app的启动时间很长可以理解,因为实际的启动需要等到dex优化完毕才能进行,这个时候就直接再等PROC_START_TIMEOUT(10s)之后再检查。我们看下mDidDexOpt的定义就知道了:
/**
* This is set if we had to do a delayed dexopt of an app before launching
* it, to increase the ANR timeouts in that case.
*/
boolean mDidDexOpt;
这里的注释写的很清楚了。在mHandler中如果不是在dex优化中的话,那么就要调用processStartTimedOutLocked方法来处理启动超时的数据和资源回收的操作了,并且需要停止当前这个任务,同时以ANR的方式通知用户,这部分的逻辑这里就不详述了。
到现在位置我们弄明白了android是怎么启动一个app进程的了,主要就是通过addAppLocked方法实现,这个方法会调用startProcessLocked方法实现,而这个方法很长,主要就是一些app进程启动数据和参数检查和设置,最后就是调用Process类的静态方法start方法和zygote建立链接fork进程。了解了AMS怎么启动一个进程之后,我们接下来分析一下AMS怎么管理进程的。
AMS的进程管理之LRU管理
前面我们在分析app进程的启动流程的时候,我们看到系统调用updateLruProcessLocked和updateOomAdjLocked来调控设置进程,其实在AMS中有很多地方都会调用到他们。我们首先来看下updateLruProcessLocked方法,这个方法用来调整某个进程在mLruProcesses列表中的位置,mLruProcesses是最近使用进程列表的意思。每当进程中的activity或者service等组件发生变化的时候,就意味着相应的进程发生了活动,因此调用这个方法将该进程调整到尽可能高的位置,同时还要更新关联进程的位置。在mLruProcesses这个列表中,最近运行的进程是从前往后排列的,也就是说越是处于前端的这个进程就越是最近使用的,同时需要注意的是拥有activity的进程的位置总是高于只有service的进程,因为activity可以和用户发生实际的交互,而后台的service是不可以的。
还要需要说明的是,在分析LRU管理机制之前,我们需要关注以下两个变量:
/**
* Where in mLruProcesses that the processes hosting activities start.
*/
int mLruProcessActivityStart = 0;
/**
* Where in mLruProcesses that the processes hosting services start.
* This is after (lower index) than mLruProcessesActivityStart.
*/
int mLruProcessServiceStart = 0;
就如同注释解释的那样,第一个变量总是指向列表中位置最高的带有activity进程和没有activity只有service的进程,并且mLruProcessServiceStart总是在mLruProcessActivityStart的后面。还需要注意的是mLruProcesses的定义:
/**
* List of running applications, sorted by recent usage.
* The first entry in the list is the least recently used.
*/
final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
我们看到mLruProcesses只是一个ArrayList,在mLruProcesses中,某个成员的index越大,就表示这个app越是最近使用的,这一点在后面的分析中很重要。
接下来我们看一下updateLruProcessLocked的实现,这个方法比较长,我们分段来分析这个方法的实现:
final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
ProcessRecord client)
final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities
|| app.treatLikeActivity;
final boolean hasService = false; // not impl yet. app.services.size() > 0;
首先我们看到这里定义一个变量hasActivity用来表示某个app中是否包含activity组件,以下三种情况都可以看做是有activity组件的:app本身确实包含activity组件;app本身有service,并且有另外一个含有activity的app链接到此app的service上;该app启动serivce的时候带有标记BIND_TREAT_LIKE_ACTIVITY。接下来的变量hasService只是定义了,但是没有实际的实现,从后面的注释我们也能看出这一点(Google的代码还是有待开发的,有很多功能还是没有完善的,android系统源码中有很多这样的代码),所以我们后面分析的时候和这个变量相关的代码可以直接跳过。接下来的代码如下:
if (!activityChange && hasActivity)
// The process has activities, so we are only allowing activity-based adjustments
// to move it. It should be kept in the front of the list with other
// processes that have activities, and we don't want those to change their
// order except due to activity operations.
return;
这里的代码中注释写的很清楚,这里的意思就是,如果当前app中有activity组件,并且不是activity发生了改变(启动或者销毁等),那就直接返回,因为这个时候没有必要更新LRU。
我们继续看下面的代码:
// 这是系统中的一个计数器,记录本方法被调用了多少次,也就是LRU更新了多少次。
mLruSeq++;
// 记录更新时间
final long now = SystemClock.uptimeMillis();
app.lastActivityTime = now;
// First a quick reject: if the app is already at the position we will
// put it, then there is nothing to do.
// 上面英文注释写的很清楚,就是查看我们要插入的app是不是已经在mLruProcesses顶端了,如果是就不用插入了。
if (hasActivity)
// 有activity的情况
final int N = mLruProcesses.size();
if (N > 0 && mLruProcesses.get(N-1) == app)
if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top activity: " + app);
return;
else
// 没有activity情况,可以暂时理解为有service的情况(真正有service的判定暂时没有实现呢!!)
if (mLruProcessServiceStart > 0
&& mLruProcesses.get(mLruProcessServiceStart-1) == app)
if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top other: " + app);
return;
上面的代码意图我的注释中已经写清楚了,基本逻辑也很简单。下面我们继续分析代码:
int lrui = mLruProcesses.lastIndexOf(app);
if (app.persistent && lrui >= 0)
// We don't care about the position of persistent processes, as long as
// they are in the list.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, persistent: " + app);
return;
这部分的代码主要就是要排除persistent app,这部分app不会被杀死,永远在运行,因此没有必要针对这样的app进程进行LRU排序。
if (lrui >= 0)
if (lrui < mLruProcessActivityStart)
mLruProcessActivityStart--;
if (lrui < mLruProcessServiceStart)
mLruProcessServiceStart--;
mLruProcesses.remove(lrui);
这部分代码逻辑也很简单,首先判断一下我们需要更新的app所在的index,如果小于mLruProcessActivityStart,那么mLruProcessActivityStart需要减1,同理如果小于mLruProcessServiceStart,那么mLruProcessServiceStart也需要减1,这两个变量的含义上面已经解释,这里操作因为我们下面即将remove掉这个app进程记录(我们要把它放到合适的地方去),所以这里都要减1.
接下来的代码就是主要操作代码了,分为3种情况操作:有activity的,有service的(没有实现,我们略过),其他的(暂时可以理解为有service的)。我们首先看一下有activity的情况:
// 这个变量指向我们操作app进程记录的下一个index
int nextIndex;
if (hasActivity)
final int N = mLruProcesses.size();
// 这个分支表示目前app没有activity,并且链接到它的service中的app有activity,这里就把这个app添加到mLruProcesses的第二位,因为第一位必须是实际包含activity的app进程
if (app.activities.size() == 0 && mLruProcessActivityStart < (N - 1))
// Process doesn't have activities, but has clients with
// activities... move it up, but one below the top (the top
// should always have a real activity).
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Adding to second-top of LRU activity list: " + app);
mLruProcesses.add(N - 1, app);
// To keep it from spamming the LRU list (by making a bunch of clients),
// we will push down any other entries owned by the app.
// 为了防止某个app中的service绑定了一群client从而导致LRU中顶部大部分都是这些client,这里需要将这些client往下移动,以防止某些app通过和某个app的service绑定从而提升自己在LRU中位置。
final int uid = app.info.uid;
for (int i = N - 2; i > mLruProcessActivityStart; i--)
ProcessRecord subProc = mLruProcesses.get(i);
if (subProc.info.uid == uid)
// We want to push this one down the list. If the process after
// it is for the same uid, however, don't do so, because we don't
// want them internally to be re-ordered.
if (mLruProcesses.get(i - 1).info.uid != uid)
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Pushing uid " + uid + " swapping at " + i + ": "
+ mLruProcesses.get(i) + " : " + mLruProcesses.get(i - 1));
ProcessRecord tmp = mLruProcesses.get(i);
mLruProcesses.set(i, mLruProcesses.get(i - 1));
mLruProcesses.set(i - 1, tmp);
i--;
else
// A gap, we can stop here.
break;
// 如果进程有activity,就把它放到顶端(很多情况下都是走这个逻辑)
else
// Process has activities, put it at the very tipsy-top.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
mLruProcesses.add(app);
nextIndex = mLruProcessServiceStart;
上面的代码展示了当某个应用的hasActivity为true的时候的逻辑,上面的注释说的很清楚,这里不再赘述,只是读者需要仔细理解一下这里的代码,才能理解其中的含义。我们继续看接下来的代码:
// 这个分支不会执行,因为hasService判定没有实现,总是为false
else if (hasService)
// Process has services, put it at the top of the service list.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);
mLruProcesses.add(mLruProcessActivityStart, app);
nextIndex = mLruProcessServiceStart;
mLruProcessActivityStart++;
// 一般就是走这个分支了,目前可以理解为有service的情况
else
// Process not otherwise of interest, it goes to the top of the non-service area.
// 我们直接定义index为mLruProcessServiceStart,因为此时的位置最高只能是mLruProcessServiceStart这个位置
int index = mLruProcessServiceStart;
// 一般情况下client都是null
if (client != null)
// If there is a client, don't allow the process to be moved up higher
// in the list than that client.
int clientIndex = mLruProcesses.lastIndexOf(client);
if (DEBUG_LRU && clientIndex < 0) Slog.d(TAG_LRU, "Unknown client " + client
+ " when updating " + app);
if (clientIndex <= lrui)
// Don't allow the client index restriction to push it down farther in the
// list than it already is.
clientIndex = lrui;
if (clientIndex >= 0 && index > clientIndex)
index = clientIndex;
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding at " + index + " of LRU list: " + app);
// 将app进程记录对象插入到index位置
mLruProcesses.add(index, app);
nextIndex = index-1;
mLruProcessActivityStart++;
mLruProcessServiceStart++;
到这里我们的LRU基本就更新完毕,下面的代码对这个列表还需要进行一定的微调:
// If the app is currently using a content provider or service,
// bump those processes as well.
for (int j=app.connections.size()-1; j>=0; j--)
ConnectionRecord cr = app.connections.valueAt(j);
if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
&& cr.binding.service.app != null
&& cr.binding.service.app.lruSeq != mLruSeq
&& !cr.binding.service.app.persistent)
nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
"service connection", cr, app);
for (int j=app.conProviders.size()-1; j>=0; j--)
ContentProviderRecord cpr = app.conProviders.get(j).provider;
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent)
nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
"provider reference", cpr, app);
这里的微调分为两种情况,第一是将和本进程的service关联的client进程的位置调整到本进程之后,第二是将和本进程ContentProvider关联的client进程位置调整到本进程之后。调整的方式都是使用updateLruProcessInternalLocked方法,我们看一下updateLruProcessInternalLocked方法的定义:
updateLruProcessInternalLocked@ActivityManagerService.java
private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
String what, Object obj, ProcessRecord srcApp)
app.lastActivityTime = now;
// 如果有activity,不做调整,因为不属于这个调整范围
if (app.activities.size() > 0)
// Don't want to touch dependent processes that are hosting activities.
return index;
int lrui = mLruProcesses.lastIndexOf(app);
// 如果进程不在mLruProcesses中就返回
if (lrui < 0)
Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
+ what + " " + obj + " from " + srcApp);
return index;
// 如果目前进程的位置高于需要调整的位置,那么不做调整,返回
if (lrui >= index)
// Don't want to cause this to move dependent processes *back* in the
// list as if they were less frequently used.
return index;
// 如果目前进程的位置比mLruProcessActivityStart还要高,无需调整
if (lrui >= mLruProcessActivityStart)
// Don't want to touch dependent processes that are hosting activities.
return index;
// 把app调整到index-1的位置
mLruProcesses.remove(lrui);
if (index > 0)
index--;
if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
+ " in LRU list: " + app);
mLruProcesses.add(index, app);
// 返回目前进程的位置
return index;
我们前面mLruProcessActivityStart调用这个方法的时候传递的index参数都是nextIndex,也就是说需要把新插入的进程记录的下一个进行微调,最高就是放到mLruProcessServiceStart的位置。
到这里我们就基本分析完了LRU的更新过程,现在我们总结一下它的流程:updateLruProcessLocked方法中调整进程很重要的一个依据就是进程有没有活动的activity,除了进程本身存在activity对象外,如果和进程中运行的serivice相关联的客户进程中有activity也算是本进程拥有activity。如果一个进程拥有activity的话,那么把这个进程放到列表的最高位置,否则只会把它放到没有activity的前面。调整某个进程的位置之后,还有调整和该进程的关联进程的位置,进程的关联进程有两种类型:绑定本进程servrice的进程和链接了本进程的ContentProvider的进程。同时,如果这些进程本身是有activity的话,那么就不调整,需要调整的是那些没有activity的进程,在updateLruProcessInternalLocked方法中会做这种调整。
AMS的进程管理之oom_adj管理
在介绍AMS的进程oom_adj管理之前,我们先了解一下什么是oom_adj。我们知道android运行的设备一般都是内存资源比较有限的嵌入式移动设备,在这些设备上的内存资源是十分宝贵的,因此我们需要妥善的使用和管理,总的来说就是:在合适的时候,将必要的内存资源释放出来。传统的linux的做法就是将不需要的进程直接杀死,为重要的进程运行腾出“空”来。但是问题来了,你根据什么来决定是否杀死一个进程呢?或者说你怎么知道某个进程是可以杀死的?仿照linux的做法,我们可以给每一个进程标记一个优先级,但是android中的进程和linux中的进程又不太一样了,进程之间可以通过binder绑定,如果杀死绑定的一端,另一端也可能会出问题。那么android是怎么做的呢,android引入了oom_adj这个东西,所谓的oom_adj就是out of memory adjustment中文叫做内存溢出调节器,这么翻译有点生硬,说的简单点这个东西就是一个用来标记某个app进程的重要性,这个值越小说明它越重要,越大就越有可能在必要的时候被“干掉”。
下面我们先看一下android中都定义了那些oom_adj的值,这些值定义在/frameworks/base/services/core/java/com/android/server/am/ProcessList.java中:
// OOM adjustments for processes in various states:
// Adjustment used in certain places where we don't know it yet.
// (Generally this is something that is going to be cached, but we
// don't know the exact value in the cached range to assign yet.)
static final int UNKNOWN_ADJ = 16;
// This is a process only hosting activities that are not visible,
// so it can be killed without any disruption.
static final int CACHED_APP_MAX_ADJ = 15;
static final int CACHED_APP_MIN_ADJ = 9;
// The B list of SERVICE_ADJ -- these are the old and decrepit
// services that aren't as shiny and interesting as the ones in the A list.
static final int SERVICE_B_ADJ = 8;
// This is the process of the previous application that the user was in.
// This process is kept above other things, because it is very common to
// switch back to the previous app. This is important both for recent
// task switch (toggling between the two top recent apps) as well as normal
// UI flow such as clicking on a URI in the e-mail app to view in the browser,
// and then pressing back to return to e-mail.
static final int PREVIOUS_APP_ADJ = 7;
// This is a process holding the home application -- we want to try
// avoiding killing it, even if it would normally be in the background,
// because the user interacts with it so much.
static final int HOME_APP_ADJ = 6;
// This is a process holding an application service -- killing it will not
// have much of an impact as far as the user is concerned.
static final int SERVICE_ADJ = 5;
// This is a 以上是关于Android ActivityManagerService(AMS)的进程管理的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向Android 权限 ( Android 逆向中使用的 android.permission 权限 | Android 系统中的 Linux 用户权限 )