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 用户权限 )

android 21 是啥版本

Android逆向-Android基础逆向(2-2)

【Android笔记】android Toast

图解Android - Android核心机制

Android游戏开发大全的目录