淺談framework之ActivityManagerService

1. 前言

OS世界的三大天尊之一,ActivityManagerService,下文將稱其AMS,他的作用包括進程管理,內存管理,組件管理等,作用毋庸置疑,學習AMS,跟蹤過程,弄懂原理,對系統開發定製有極大的幫助,對應用開發也可以借鑑他的架構設計自己的獨有模式,並且可以全局分析上層可能出現的問題。有時間就填坑吧,哈哈哈。

2. UI相關

作爲Android系統核心應用,SystemUI負責反饋系統及應用狀態並與用戶保持大量的交互。耳熟能詳的三欄:StatusBar(狀態欄)、NavigationBar(導航欄)與Notification Panel(通知欄),以及Recents(近期任務界面),使用起來方便又快捷。另外Keyguard(鎖屏界面)也是屬於SystemUI的一部分。並且在Android8.0代碼中,Keyguard模塊已經從外部被合併到SystemUI源碼目錄下。

SystemUI 啓動

Launcher 啓動

3. 任務棧相關

任務棧

4. 組件相關

AMS與四大組件的註冊,啓動,註銷相關,這裏只說一下Activity組件的註冊,啓動,註銷過程,此過程大體如下:

在這裏插入圖片描述

中間細節圖如下:
在這裏插入圖片描述
上圖是根據android4.4.2所繪,中間還有一個細節需要提一下,如下:
framworks/base/services/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);

        r.task.stack.setLaunchTime(r);
         //1
        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, mService.mProcessStats);
                }
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }
        }
         //2
        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);
    }

從註釋1的判斷來看,說明當前的Activity所在的進程存在的話,則執行realStartActivityLocked,也就是上圖所繪流程。當前Activity所在的進程不存在的情況了則走註釋2處的startProcessLocked的函數。

這裏與app的冷啓動與熱啓動相關。這部分之前有寫過相關的內容,下面我則挪過來用好了,懶~

4.1 客戶端請求

frameworks/base/services/java/com/android/server/am/ActivityManagerService.java

private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
    try {
       ......
      // Start the process.  It will either succeed and return a result containing
      // the PID of the new process, or else throw a RuntimeException.
     Process.ProcessStartResult startResult= Process.start("android.app.ActivityThread",
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, null);
      .......
    }
}

===========/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[] zygoteArgs) {
try {
   return startViaZygote(processClass, niceName, uid, gid, gids,
          debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs);
 } catch (ZygoteStartFailedEx ex) {
            Log.e(LOG_TAG,
                    "Starting VM process through Zygote failed");
            throw new RuntimeException(
                    "Starting VM process through Zygote failed", ex);
   }
}

===========/frameworks/base/core/java/android/os/Process.java =========

private static ProcessStartResult startViaZygote(final String processClass,
                                  final String niceName,
                                  final int uid, final int gid,
                                  final int[] gids,
                                  int debugFlags, int mountExternal,
                                  int targetSdkVersion,
                                  String seInfo,
                                  String[] extraArgs)
                                  throws ZygoteStartFailedEx {
        synchronized(Process.class) {
            ……
            return zygoteSendArgsAndGetResult(argsForZygote);
        }
}

startViaZygote的絕大部分代碼都在處理傳遞到Zygote中的參數,與Zygote通信通過zygoteSendArgsAndGetResult()方法完成:
===========/frameworks/base/core/java/android/os/Process.java =========

private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList<String> args)
            throws ZygoteStartFailedEx {
        openZygoteSocketIfNeeded();//確保和Zygote通信的socket已被打開
        try {
            sZygoteWriter.write(Integer.toString(args.size()));
            sZygoteWriter.newLine();
            int sz = args.size();
            for (int i = 0; i < sz; i++) {//發送請求參數到Zygote
                String arg = args.get(i);
                if (arg.indexOf('\n') >= 0) {
                    throw new ZygoteStartFailedEx(
                            "embedded newlines not allowed");
                }
                sZygoteWriter.write(arg);
                sZygoteWriter.newLine();
            }
            sZygoteWriter.flush();
            ProcessStartResult result = new ProcessStartResult();
            result.pid = sZygoteInputStream.readInt();  
          if (result.pid < 0) {
                throw new ZygoteStartFailedEx("fork() failed");
            }
            result.usingWrapper = sZygoteInputStream.readBoolean();
            return result;
        } catch (IOException ex) {
            try {
                if (sZygoteSocket != null) {
                    sZygoteSocket.close();
                }
            } catch (IOException ex2) {
                // we're going to fail anyway
                Log.e(LOG_TAG,"I/O exception on routine close", ex2);
            }
            sZygoteSocket = null;
            throw new ZygoteStartFailedEx(ex);
        }
    }


4.2 處理客戶端請求

首先是到ZygoteInit.java main函數這裏開始觀察
===/frameworks/base/core/java/com/android/internal/os/ZygoyeInit.java ====

public static void main(String argv[]) {
	……
	runSelectLoop();
	……
}

核心代碼在runSelectLoop函數的實現中,繼續往下看

===/frameworks/base/core/java/com/android/internal/os/ZygoyeInit.java ====

private static void runSelectLoop() throws MethodAndArgsCaller {
	……
	white(true){
			……
		    if (index < 0) {
                throw new RuntimeException("Error in select()");
            } else if (index == 0) {
                ZygoteConnection newPeer = acceptCommandPeer();
                peers.add(newPeer);
                fds.add(newPeer.getFileDesciptor());
            } else {
                boolean done;
                done = peers.get(index).runOnce();
                if (done) {
                    peers.remove(index);
                    fds.remove(index);
                }
            }
	}
}

上面函數實現中,最終會調用到runOnce函數

===frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java ===

boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
        String args[];
        Arguments parsedArgs = null;
        FileDescriptor[] descriptors;
        try {
            args = readArgumentList();//讀取客戶端發送過來的參數
            descriptors = mSocket.getAncillaryFileDescriptors();
        } catch (IOException ex) {
            Log.w(TAG, "IOException on command socket " + ex.getMessage());
            closeSocket();
            return true;
        }
        if (args == null) {
            // EOF reached.
            closeSocket();
            return true;
        }
        /** the stderr of the most recent request, if avail */
        PrintStream newStderr = null;
        if (descriptors != null && descriptors.length >= 3) {
            newStderr = new PrintStream(
                    new FileOutputStream(descriptors[2]));
        }
        int pid = -1;
        FileDescriptor childPipeFd = null;
        FileDescriptor serverPipeFd = null;
        try {
            parsedArgs = new Arguments(args);
            applyUidSecurityPolicy(parsedArgs, peer, peerSecurityContext);
            applyRlimitSecurityPolicy(parsedArgs, peer, peerSecurityContext);
            applyCapabilitiesSecurityPolicy(parsedArgs, peer, peerSecurityContext);
            applyInvokeWithSecurityPolicy(parsedArgs, peer, peerSecurityContext);
            applyseInfoSecurityPolicy(parsedArgs, peer, peerSecurityContext);
            applyDebuggerSystemProperty(parsedArgs);
            applyInvokeWithSystemProperty(parsedArgs);
            int[][] rlimits = null;
            if (parsedArgs.rlimits != null) {
                rlimits = parsedArgs.rlimits.toArray(intArray2d);
            }
            if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) {
                FileDescriptor[] pipeFds = Libcore.os.pipe();
                childPipeFd = pipeFds[1];
                serverPipeFd = pipeFds[0];
                ZygoteInit.setCloseOnExec(serverPipeFd, true);
            }
            //fork一個新進程
pid=Zygote.forkAndSpecialize(parsedArgs.uid,  parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, rlimits,parsedArgs.mountExternal, parsedArgs.seInfo,      parsedArgs.niceName);
        } catch (IOException ex) {
            logAndPrintError(newStderr, "Exception creating pipe", ex);
        } catch (ErrnoException ex) {
            logAndPrintError(newStderr, "Exception creating pipe", ex);
        } catch (IllegalArgumentException ex) {
            logAndPrintError(newStderr, "Invalid zygote arguments", ex);
        } catch (ZygoteSecurityException ex) {
            logAndPrintError(newStderr,
                    "Zygote security policy prevents request: ", ex);
        }
        try {
            if (pid == 0) {//子進程
                // in child
                IoUtils.closeQuietly(serverPipeFd);
                serverPipeFd = null;
                handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
                // should never get here, the child is expected to either
                // throw ZygoteInit.MethodAndArgsCaller or exec().
                return true;
            } else {//父進程
                // in parent...pid of < 0 means failure
                IoUtils.closeQuietly(childPipeFd);
                childPipeFd = null;
                return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
            }
        } finally {
            IoUtils.closeQuietly(childPipeFd);
            IoUtils.closeQuietly(serverPipeFd);
        }
}

===frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java ===

private void handleChildProc(Arguments parsedArgs,
            FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
            throws ZygoteInit.MethodAndArgsCaller {
        closeSocket();//關閉子進程中,從Zygote fork過來的服務端socket
        ZygoteInit.closeServerSocket();
        .....
        if (parsedArgs.niceName != null) {
            Process.setArgV0(parsedArgs.niceName);
        }
        if (parsedArgs.runtimeInit) {
            if(parsedArgs.invokeWith!=null) {WrapperInit.execApplication(parsedArgs.invokeWith,
                        parsedArgs.niceName, parsedArgs.targetSdkVersion,
                        pipeFd, parsedArgs.remainingArgs);
            } else {
                RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
                        parsedArgs.remainingArgs);
            }
        } else {
        	......
        }
    }

===frameworks/base/core/java/com/android/internal/os/RuntimeInit.java ===

    public static final void zygoteInit(int targetSdkVersion, String[] argv)
            throws ZygoteInit.MethodAndArgsCaller {
        if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");

        redirectLogStreams();

        commonInit();
        nativeZygoteInit();

        applicationInit(targetSdkVersion, argv);
    }

===frameworks/base/core/java/com/android/internal/os/RuntimeInit.java ==

private static void applicationInit(int targetSdkVersion, String[] argv)
            throws ZygoteInit.MethodAndArgsCaller {
        // If the application calls System.exit(), terminate the process
        // immediately without running any shutdown hooks.  It is not possible to
        // shutdown an Android application gracefully.  Among other things, the
        // Android runtime shutdown hooks close the Binder driver, which can cause
        // leftover running threads to crash before the process actually exits.
        nativeSetExitWithoutCleanup(true);

        // We want to be fairly aggressive about heap utilization, to avoid
        // holding on to a lot of memory that isn't needed.
        VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
        VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);

        final Arguments args;
        try {
            args = new Arguments(argv);
        } catch (IllegalArgumentException ex) {
            Slog.e(TAG, ex.getMessage());
            // let the process exit
            return;
        }

        // Remaining arguments are passed to the start class's static main
        invokeStaticMain(args.startClass, args.startArgs);
}

===frameworks/base/core/java/com/android/internal/os/RuntimeInit.java ===

 private static void invokeStaticMain(String className, String[] argv)
            throws ZygoteInit.MethodAndArgsCaller {
        Class<?> cl;

        try {
            cl = Class.forName(className);
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException(
                    "Missing class when invoking static main " + className,
                    ex);
        }
        Method m;
        try {
            m = cl.getMethod("main", new Class[] { String[].class });
        } catch (NoSuchMethodException ex) {
            throw new RuntimeException(
                    "Missing static main on " + className, ex);
        } catch (SecurityException ex) {
            throw new RuntimeException(
                    "Problem getting static main on " + className, ex);
        }

        int modifiers = m.getModifiers();
          if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
            throw new RuntimeException(
                    "Main method is not public and static on " + className);
          }
        /*
         * This throw gets caught in ZygoteInit.main(), which responds
         * by invoking the exception's run() method. This arrangement
         * clears up all the stack frames that were required in setting
         * up the process.
         */
        throw new ZygoteInit.MethodAndArgsCaller(m, argv);
    }

最終是通過反射調用到ActivityThread.java的main函數中。

5. 進程相關

5.1 進程分類

1、foreground process
殺死foreground需要用戶響應,因爲這個安全優先級是最高的
是用戶操作所必須的,任一時間下,僅有少數進程會處於前臺,僅當內存實在無法供給它們維持同時運行時纔會被殺死。
2、visible process
activity不在前端顯示,但也沒有完全隱藏,能夠看得見,比如彈出一個對話框
可視進程依然被視爲是很重要的,非到不殺死它們便無法維持前臺進程運行時,纔會被殺死。
3、Service process
正在運行的,不在上述兩種狀態的service
是由 startService() 方法啓動的服務,它不會變成上述兩類。儘管服務進程不會直接爲用戶所見,但它們一般都在做着用戶所關心的事情(比如在後臺播放mp3或者從網上下載東 西)。所以系統會盡量維持它們的運行,除非系統內存不足以維持前臺進程和可視進程的運行需要。
4、background process
不可見狀態的activity進程,onstop被調用
包含目前不爲用戶所見的activity(Activity對象的 onStop() 方法已被調用)。這些進程與用戶體驗沒有直接的聯繫,可以在任意時間被殺死以回收內存供前臺進程、可視進程以及服務進程使用。一般來說,會有很多背景進程 運行,所以它們一般存放於一個LRU(最後使用)列表中以確保最後被用戶使用的activity最後被殺死。
5、empty process
沒有運行任何component的進程,保留這個進程主要是爲了緩存的需要

5.2 進程管理

關於Android系統的內存回收機制,相信大家都不陌生,Android基於各個應用進程承載四大組件的狀態對應用進程進行重要性評估,並在系統內存緊張時根據重要性由低到高來選擇殺死應用進程,以達到釋放內存的目的。重要性評估由AMS執行,具體來說就是由updateOomAdjLocked函數決定,這個函數作用就是更新應用進程的重要性。

應用進程(ProcessRecord)的重要性由三個狀態值表示:

adj:LMK殺進程的評分依據
procState:指示進程狀態
schedGroup:指示進程調度策略

framworks/base/services/java/com/android/server/am/ActivityManagerService.java

    final void updateOomAdjLocked() {
		//此處省略部分代碼
        for (int i=N-1; i>=0; i--) {
            ProcessRecord app = mLruProcesses.get(i);
            if (!app.killedByAm && app.thread != null) {
                app.procStateChanged = false;
                final boolean wasKeeping = app.keeping;
                //關注點1
                computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now); 
                //此處省略部分代碼
				//關注點2
                applyOomAdjLocked(app, wasKeeping, TOP_APP, true, false, now); 

                // Count the number of process types.
                switch (app.curProcState) {
                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                        mNumCachedHiddenProcs++;
                        numCached++;
                        if (numCached > cachedProcessLimit) {
                        	//關注點3
                            killUnneededProcessLocked(app, "cached #" + numCached);
                        }
                        break;
                    case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
                        if (numEmpty > ProcessList.TRIM_EMPTY_APPS
                                && app.lastActivityTime < oldTime) {
                            killUnneededProcessLocked(app, "empty for "
                                    + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime)
                                    / 1000) + "s");
                        } else {
                            numEmpty++;
                            if (numEmpty > emptyProcessLimit) {
                                killUnneededProcessLocked(app, "empty #" + numEmpty);
                            }
                        }
                        break;
                    default:
                        mNumNonCachedProcs++;
                        break;
                }
			//此處省略部分代碼
    }

1、調用computeOomAdjLocked來計算進程的oom_adj的值;

2、調用applyOomAdjLocked來更新進程的oom_adj的值;

3、調用killUnneededProcessLocked來kill掉不適用的進程。

updateOomAdjLocked在後面還做了一些對內存的處理優化操作,這裏就不介紹了;這裏有一個疑問,底層和上層都有kill進程的過程,其中有什麼區別呢,我的想法是這樣的,底層的kill主要發生在進程出現Low Memory的時候纔去做,而上層在AMS中是系統在檢測到內存不足時清理緩存的一個重要過程。

下面再看上面關注點2調用的函數:

private final boolean applyOomAdjLocked(ProcessRecord app, boolean wasKeeping,
            ProcessRecord TOP_APP, boolean doingAll, boolean reportingProcessState, long now) {
       //省略
        if (app.curAdj != app.setAdj) {
            if (Process.setOomAdj(app.pid, app.curAdj)) {
                if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(
                    TAG, "Set " + app.pid + " " + app.processName +
                    " adj " + app.curAdj + ": " + app.adjType);
                app.setAdj = app.curAdj;
            } else {
                success = false;
                Slog.w(TAG, "Failed setting oom adj of " + app + " to " + app.curAdj);
            }
        }
       //省略
        return success;
    }

curAdj是computeOomAdjLocked計算出的adj值,通過調用Process.setOomAdj(int pid, int amt)往下進行設置。
updateOomAdjLocked在後面還做了一些對內存的處理優化操作,這裏就不介紹了;這裏有一個疑問,底層和上層都有kill進程的過程,其中有什麼區別呢,我的想法是這樣的,底層的kill主要發生在進程出現Low Memory的時候纔去做,而上層在AMS中是系統在檢測到內存不足時清理緩存的一個重要過程。

public static final native boolean setOomAdj(int pid, int amt);

5.3 底層實現

framworks/base/core/jni/android_util_Process.cpp

jboolean android_os_Process_setOomAdj(JNIEnv* env, jobject clazz,
                                      jint pid, jint adj)
{
#ifdef HAVE_OOM_ADJ
    char text[64];
    sprintf(text, "/proc/%d/oom_adj", pid);
    int fd = open(text, O_WRONLY);
    if (fd >= 0) {
        sprintf(text, "%d", adj);
        write(fd, text, strlen(text));
        close(fd);
    }
    return true;
#endif
    return false;
}

可以看到這段函數打開了對應的位置的oom_adj文件,並將adj的值寫到這個文件中,也即是達到了更新對應進程oom_adj值的效果。

5.4 LMK原理

內核LMK的原理很簡單:首先註冊了shrinker,在內存緊張的時候會觸發lowmem_shrink(linux 高版本是lowmem_scan)方法,這個方法要做的就是找到一個進程,然後殺掉他,釋放一些內存。

獲取剩餘內存的大小,和Minfree內存閥值做比較,找到對應的內存閥值,找到對應的adj值。
遍歷所有的進程,大於該adj的值的進程是要殺掉的目標進程, 但是並不是全部殺掉,而是找到adj最大的進程殺掉,如果最大adj有多個相同adj進程,則殺掉佔用內存最大的一個。

5.5 後記

andorid 5.0及其後面版本跟上面會有一定差異,最大的區別是與驅動通信的方式的改變。
整個流程圖如下(網圖,自己懶得畫了):
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章