ActivityManagerService解读之进程管理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/abm1993/article/details/82111416

简介

ActivityManagerService(后续简称为ams)是Android系统框架层中的一个很重要的服务,负责Android四大组建的启动和管理,进程的创建和调度。接下来,咱们今天就来聊聊ams对进程的创建和调度。参考于Android P代码。

进程

Android系统将尽可能的长时间的保持应用进程,当Android系统内存不足的时候,为了新建进程或运行更重要的进程,最终需要移除旧进程来回收内存,Android可能会决定在某一时刻关闭某一进程,决定终止哪一个进程时,Android系统将权衡它们对用户的相对重要程度。例如:相对于托管可Activity的进程而言,它更有可能关闭托管屏幕上不再可见的Activity的进程。因此,是否终止某个进程的决定取决与该进程中所运行组件的状态。

当某个应用组件启动且该应用没有运行其他任何组件时,Android 系统会使用单个执行线程为应用启动新的 Linux 进程。默认情况下,同一应用的所有组件在相同的进程和线程(称为“主”线程)中运行。 如果某个应用组件启动且该应用已存在进程(因为存在该应用的其他组件),则该组件会在此进程内启动并使用相同的执行线程。默认情况下,同一应用的所有组件均在相同的进程中运行,且大多数应用都不会改变这一点。

如何创建启动进程

当我们首次启动Android的四大组建时,ams会判断系统中是否存在承载该组件的进程,如果不存在则通过startProcessLocked方法进行创建,以下就是启动四大组件,进程创建的时机(略去大部分代码,只留取了关键代码)

    //启动Service时创建process
    //frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired)
            throws TransactionTooLargeException {
        // Not running -- get it started, and enqueue this service record
        // to be executed when the app comes up.
        if (app == null && !permissionsReviewRequired) {
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    hostingType, r.name, false, isolated, false, r.latestCallerName)) == null) {
                String msg = "Unable to launch app "
                        + r.appInfo.packageName + "/"
                        + r.appInfo.uid + " for service "
                        + r.intent.getIntent() + ": process is bad";
                Slog.w(TAG, msg);
                bringDownServiceLocked(r);
                return msg;
            }
            if (isolated) {
                r.isolatedProc = app;
            }
        }
    }

    //启动Activity时,创建process 
    //frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
    void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);

        getLaunchTimeTracker().setLaunchTime(r);

        if (app != null && app.thread != null) {
            try {
                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                        || !"android".equals(r.info.packageName)) {
                    // Don't add this if it is a platform component that is marked
                    // to run in multiple processes, because this is actually
                    // part of the framework so doesn't make sense to track as a
                    // separate apk in the process.
                    app.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode,
                            mService.mProcessStats);
                }
                // When resumeTopActivityInnerLocked is called, app is not decided yet.
                // Thus, set app member here.
                app.bindAppByVisible = false;
                app.prevAdjType = app.adjType;
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }

            // If a dead object exception was thrown -- fall through to
            // restart the application.
        }

        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true,
                (null != r.launchedFromPackage ? r.launchedFromPackage : "NA"));
    }

    //发送广播时,创建process
    //frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
    final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
        if ((r.curApp=mService.startProcessLocked(targetProcess,
                info.activityInfo.applicationInfo, true,
                r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
                "broadcast", r.curComponent,
                (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false,
                            cause)) == null) {
            // Ah, this recipient is unavailable.  Finish it if necessary,
            // and mark the broadcast record as ready for the next.
            Slog.w(TAG, "Unable to launch app "
                    + info.activityInfo.applicationInfo.packageName + "/"
                    + receiverUid + " for broadcast "
                    + r.intent + ": process is bad");
            logBroadcastReceiverDiscardLocked(r);
            finishReceiverLocked(r, r.resultCode, r.resultData,
                    r.resultExtras, r.resultAbort, false);
            scheduleBroadcastsLocked();
            r.state = BroadcastRecord.IDLE;
            return;
        }
    }

    //contentprovider处理过程中,创建进程
    private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
            String name, IBinder token, boolean stable, int userId) {
                        // Use existing process if already started
                        checkTime(startTime, "getContentProviderImpl: looking for process record");
                        ProcessRecord proc = getProcessRecordLocked(
                                cpi.processName, cpr.appInfo.uid, false);
                        if (proc != null && proc.thread != null && !proc.killed) {
                            if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
                                    "Installing in existing process " + proc);
                            if (!proc.pubProviders.containsKey(cpi.name)) {
                                checkTime(startTime, "getContentProviderImpl: scheduling install");
                                proc.pubProviders.put(cpi.name, cpr);
                                try {
                                    proc.thread.scheduleInstallProvider(cpi);
                                } catch (RemoteException e) {
                                }
                            }
                        } else {
                            checkTime(startTime, "getContentProviderImpl: before start process");
                            String cause = r != null ? r.processName : "";
                            proc = startProcessLocked(cpi.processName,
                                    cpr.appInfo, false, 0, "content provider",
                                    new ComponentName(cpi.applicationInfo.packageName,
                                            cpi.name), false, false, false, cause);
                            checkTime(startTime, "getContentProviderImpl: after start process");
                            if (proc == null) {
                                Slog.w(TAG, "Unable to launch app "
                                        + cpi.applicationInfo.packageName + "/"
                                        + cpi.applicationInfo.uid + " for provider "
                                        + name + ": process is bad");
                                return null;
                            }
                        }
    }


开发测试中,我们最直观的便是通过log,来确定它是否需要新创建进程,对应关键log如下

2018-08-21 11:45:08.113  1602  1669 I am_proc_start: [0,18743,10013,android.process.acore,content provider:com.google.android.apps.messaging,com.android.providers.contacts/.ContactsProvider2]
2018-08-21 11:45:08.113  1602  1669 I ActivityManager: Start proc 18743:android.process.acore/u0a13 for content provider com.android.providers.contacts/.ContactsProvider2

进程管理

ams对进程的管理主要体现于两个方面,一调整进程在mLruProcesses集合中的位置,二调整进程的oom值依据一些策略进行最基本的内存回收,前者相对简单,后者通过oom值来权衡其对于用户的相对重要程度,决定在某一个时刻关闭某一个进程。Android根据进程的重要程度,将其分为五个层次:

  1. 前台进程
    用户当前操作所必需的进程。如果一个进程满足以下任一条件,即视为前台进程:
        托管用户正在交互的 Activity(已调用 Activity 的 onResume() 方法)
        托管某个 Service,后者绑定到用户正在交互的 Activity
        托管正在“前台”运行的 Service(服务已调用 startForeground())
        托管正执行一个生命周期回调的 Service(onCreate()、onStart() 或 onDestroy())
        托管正执行其 onReceive() 方法的 BroadcastReceiver
  2. 可见进程
    没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。 如果一个进程满足以下任一条件,即视为可见进程:
        托管不在前台、但仍对用户可见的 Activity(已调用其 onPause() 方法)。例如,如果前台 Activity 启动了一个对话框,允许在其后显示上一 Activity,则有可能会发生这种情况。
        托管绑定到可见(或前台)Activity 的 Service。
    可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。 
  3. 服务进程
    正在运行已使用 startService() 方法启动的服务且不属于上述两个更高类别进程的进程。尽管服务进程与用户所见内容没直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。 
  4. 后台进程
    包含目前对用户不可见的 Activity 的进程(已调用 Activity 的 onStop() 方法)。
  5. 空进程
    不含任何活动应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。 

针对以上五层重要性层次结构,Android更以细分为ADJ阀值,配合ADJAndroid在内核空间有lowmemorykiller进行内存的回收。在framework层只负责计算更新进程的oom值,下面是我手上一台小米8的oom等级数据

OOM levels:
    -900: SYSTEM_ADJ (   73,728K)
    -800: PERSISTENT_PROC_ADJ (   73,728K)
    -700: PERSISTENT_SERVICE_ADJ (   73,728K)
      0: FOREGROUND_APP_ADJ (   73,728K)
     100: VISIBLE_APP_ADJ (   92,160K)
     200: PERCEPTIBLE_APP_ADJ (  110,592K)
     300: BACKUP_APP_ADJ (  129,024K)
     400: HEAVY_WEIGHT_APP_ADJ (  221,184K)
     500: SERVICE_ADJ (  221,184K)
     600: HOME_APP_ADJ (  221,184K)
     700: PREVIOUS_APP_ADJ (  221,184K)
     800: SERVICE_B_ADJ (  221,184K)
     900: CACHED_APP_MIN_ADJ (  221,184K)
     906: CACHED_APP_MAX_ADJ (  322,560K)
前台进程ADJ=0,可见进程ADJ=100,服务进程ADJ=500,后台进程ADJ>700,空进程900~906

ams通过updateLruProcessLocked更新进程在LRU集合中的位置,通过updateOomAdjLocked,computeOomAdjLocked,applyOomAdjLocked来更新设置进程对应的oom值。下面我们便详细分析这四个关键方法。

核心方法

updateLruProcessLocked,下面是该方法的调用,可以通过android-studio全局搜索获取: (以下对LRU集合的优先级描述特指某个进程在LRU集合中的位置,该进程的索引越大,优先级越大)

frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
    bindServiceLocked(IApplicationThread, IBinder, Intent, String, IServiceConnection, int, String, ...)
    realStartServiceLocked(ServiceRecord, ProcessRecord, boolean)
    removeConnectionLocked(ConnectionRecord, ProcessRecord, ActivityRecord)
    setServiceForegroundInnerLocked(ServiceRecord, int, Notification, int)
    unbindServiceLocked(IServiceConnection)
    updateServiceClientActivitiesLocked(ProcessRecord, ConnectionRecord, boolean)
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
    addAppLocked(ApplicationInfo, String, boolean, boolean, String)
    attachApplicationLocked(IApplicationThread, int, int, long)
    getContentProviderImpl(IApplicationThread, String, IBinder, boolean, int)
    setSystemProcess()
    updateLruProcessLocked(ProcessRecord, boolean, ProcessRecord)
frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
    destroyActivityLocked(ActivityRecord, boolean, String)
    resumeTopActivityInnerLocked(ActivityRecord, ActivityOptions)
frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
    realStartActivityLocked(ActivityRecord, ProcessRecord, boolean, boolean)
frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
    realStartActivityLocked(ActivityRecord, ProcessRecord, boolean, boolean)
frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
    processCurBroadcastLocked(BroadcastRecord, ProcessRecord, boolean)

通过搜索updateLruProcessLocked调用的地方我们可以发现,四大组件启动创建进程时都会更新mLruProcesses集合,四大组件状态改变时也会更新mLruProcesses集合,对于某些情况下创建的进程也会通过addAppLocked调用updateLruProcessLocked更新集合。下面我们就看看updateLruProcessLocked的具体实现

    final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
            ProcessRecord client) {
        final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities
                || app.treatLikeActivity || app.recentTasks.size() > 0;
        final boolean hasService = false; // not impl yet. app.services.size() > 0;
        if (!activityChange && hasActivity) {
            //这里可以看出四大组件中持有activity的进程在lru集合中的位置比较靠前
            //activityChange为false,并且该进程存在activity的时候,说明进程中没有activity状态改变,无需进行位置调整
            // 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;
        }

        mLruSeq++;//记录updateLruProcessLocked调用的次数
        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.
        if (hasActivity) {
            final int N = mLruProcesses.size();
            if (N > 0 && mLruProcesses.get(N-1) == app) {
                //有activity情况下,集合的最近使用的app就是当前app,无需进行调整直接返回
                if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top activity: " + app);
                return;
            }
        } else {
            //lru集合我们可以分为两个部分:
            //由启动activity创建的进程,mLruProcessActivityStart标记其索引
            //由启动activity创建的进程,mLruProcessServiceStart标记其索引,比mLruProcessActivityStart小
            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);//获取集合中最后一次出现该app的索引,如果不包含值为-1

        if (app.persistent && lrui >= 0) {//对于系统中的persistent=true的进程,不做调整
            // 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;
        }

        if (lrui >= 0) {
            //根据lrui对mLruProcessActivityStart,mLruProcessServiceStart进行调整,为什么呢?
            //因为我们需要先从列表中移除当前需要的调整的app,所以对应的指引着ActivityStart,ServiceStart就要根据需要-1
            if (lrui < mLruProcessActivityStart) {
                mLruProcessActivityStart--;
            }
            if (lrui < mLruProcessServiceStart) {
                mLruProcessServiceStart--;
            }

        int nextIndex;
        if (hasActivity) {//该进程存在activity的情况,
            final int N = mLruProcesses.size();
            if ((app.activities.size() == 0 || app.recentTasks.size() > 0)
                    && mLruProcessActivityStart < (N - 1)) {
                //根据源码的注释,我们看出该情况应该属于,当前的app中未包含activity,应该属于某个含有activity的服务端进程
                //一般情况下,我们可以这么理解,客户端的进程重要程度应该大于服务端,因为客户端相对于用户而言更重要。
                //我们把服务端进行放于客户端进程之后
                // 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.
                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;
                    }
                }
            } 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;
        } 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++;
        } else  {//不包含activity的情况
            // Process not otherwise of interest, it goes to the top of the non-service area.
            int index = mLruProcessServiceStart;
            if (client != null) {//对于bindservice的情况,我们坚持一个原则,客户端的重要性大于服务端
                // 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;//此处index就是要调整的位置,保证列表中客户端进程重要性大于服务端
                }
            }
            if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding at " + index + " of LRU list: " + app);
            //要知道当前需要调整的是服务端进程,在不存在客户端进程的情况下,直接使用
            //mLruProcessServiceStart进行调整,存在的情况下,保证客户端的优先级大于服务端
            mLruProcesses.add(index, app);
            nextIndex = index-1;
            mLruProcessActivityStart++;
            mLruProcessServiceStart++;
        }

        // 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);
            }
        }
    }


    private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
            String what, Object obj, ProcessRecord srcApp) {
        app.lastActivityTime = now;

        if (app.activities.size() > 0 || app.recentTasks.size() > 0) {
            // Don't want to touch dependent processes that are hosting activities.
            return index;//当前app存在activity的情况,不做调整,因为activity状态未改变
        }

        int lrui = mLruProcesses.lastIndexOf(app);
        if (lrui < 0) {//lru集合中不存在该进程,并不做调整
            Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
                    + what + " " + obj + " from " + srcApp);
            return index;
        }

        if (lrui >= index) {//在集合位置中的位置大于当前调整的index,也不做调整
            // Don't want to cause this to move dependent processes *back* in the
            // list as if they were less frequently used.
            return index;
        }

        if (lrui >= mLruProcessActivityStart) {//同样持有activity,不做调整
            // Don't want to touch dependent processes that are hosting activities.
            return index;
        }

        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;
    }

从更新进程在lru集合中位置的算法中,我们大致可以总结出两个特点:1.持有activity的进程总是优先于持有service的进程,2.对于存在客户端服务端的进程,客户端优先于服务端。阅读源码我们发现往往updateLruProcessLocked和updateOomAdjLocked都是一起调用的,由于updateOomAdjLocked方法代码较多十分庞大,整体逻辑较为好理解,这里就不去陈列代码了,大致介绍一下他的原理(其中涉及很多设置进程相关的细节),后续重开一片文章详细分析。updateOomAdjLocked方法,主要是用来更新进程的oom值和依据一定的策略杀进程进行内存回收。前面我们所说ams对进程的管理大致可分为调整lru列表,更新进程oom。前者用来缓存进程,目的更多的偏向于Android的设计原则“尽可能的使进程活的更长”。但是缓存也是有上限的,以我使用的小米8为例:

CUR_MAX_CACHED_PROCESSES=32 //最大缓存的进程,此值可以在开发者选项中进行设置调整
CUR_MAX_EMPTY_PROCESSES=16  //最大持有的空进程

因此前者只负责调整lru集合各进程的位置,后者在更新oom的同时,也会根据我们所设置的上限和他们的oom值去动态的杀掉多余的进程(当具有相同的oom值时,先杀占用内存多的)。而更新进程oom值,目的也在于配合lowmemorykill在系统内存不足时合理的回收内存。updateOomAdjLocked整个执行流程可以简单描述为:计算进程的oom值,设置进程的oom值。

猜你喜欢

转载自blog.csdn.net/abm1993/article/details/82111416