深刻Android系統(九)Android系統的核心-SystemServer進程

SystemServerAndroid系統的核心之一,大部分Android提供的服務都運行在這個進程裏。java

爲了防止應用進程對系統形成破壞,Android的應用進程沒有權限直接訪問設備的底層資源,只能經過SystemServer中的服務代理訪問。android

本篇重點是瞭解SystemServer的啓動過程以及它的Watchdog模塊shell

SystemServer的建立過程

SystemServer的建立能夠分爲兩部分:bootstrap

  • Zygote進程中fork並初始化SystemServer進程的過程
  • 執行SystemServer類的main方法來啓動系統服務的過程

建立SystemServer進程

Zygote進程一篇中咱們已經知道init.rc文件中定義了Zygote進程的啓動參數,以下:設計模式

service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    class main
    ......
複製代碼

其中定義了--start-system-server參數,所以,在ZygoteInit類的main方法中,會執行到forkSystemServer()函數:markdown

if (startSystemServer) {
    Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
    // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
    // child (system_server) process.
    if (r != null) {
        r.run();
        return;
    }
}
複製代碼

forkSystemServer()函數以下:多線程

private static Runnable forkSystemServer(String abiList, String socketName, ZygoteServer zygoteServer) {
        ......
        /* Hardcoded command line to start the system server */
        String args[] = {
            "--setuid=1000",
            "--setgid=1000",
            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
            "--capabilities=" + capabilities + "," + capabilities,
            "--nice-name=system_server",
            "--runtime-args",
            "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
            "com.android.server.SystemServer",
        };
        ......
        int pid;
        try {
            ......
            /* Request to fork the system server process */
            pid = Zygote.forkSystemServer(
                    parsedArgs.uid, parsedArgs.gid,
                    parsedArgs.gids,
                    parsedArgs.runtimeFlags,
                    null,
                    parsedArgs.permittedCapabilities,
                    parsedArgs.effectiveCapabilities);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }
        /* For child process */
        if (pid == 0) {
            ......
            zygoteServer.closeServerSocket();
            return handleSystemServerProcess(parsedArgs);
        }
        return null;
    }
複製代碼

在上面的方法中,主要作了以下事項:app

  • 準備Systemserver的啓動參數:
    • 進程ID組ID設置爲 1000
    • 設定進程名稱爲system_server
    • 指定Systemserver的執行類com.android.server.SystemServer
  • 調用Zygote類的forkSystemServer()函數forkSystemServer進程
    • Zygote類也是經過native層的函數來完成實際的工做,函數以下
    static jint com_android_internal_os_Zygote_nativeForkSystemServer(...) {
        pid_t pid = ForkAndSpecializeCommon(...);
        if (pid > 0) {
            // The zygote process checks whether the child process has died or not.
            ALOGI("System server process %d has been created", pid);
            gSystemServerPid = pid;
            // There is a slight window that the system server process has crashed
            // but it went unnoticed because we haven't published its pid yet. So
            // we recheck here just to make sure that all is well.
            int status;
            if (waitpid(pid, &status, WNOHANG) == pid) {
                ALOGE("System server process %d has died. Restarting Zygote!", pid);
                RuntimeAbort(env, __LINE__, "System server process has died. Restarting Zygote!");
            }
            ......
        }
        return pid;
    }
    複製代碼
    • native層的函數來看,對於SystemServer進程的forkZygote進程會經過waitpid()函數來檢查SystemServer進程是否啓動成功,若是不成功 ,Zygote進程會退出重啓
    • 相關的細節在native層ForkAndSpecializeCommon()中:
    static pid_t ForkAndSpecializeCommon(......) {
        SetSignalHandlers();
        ......
    }
    static void SetSignalHandlers() {
        struct sigaction sig_chld = {};
        sig_chld.sa_handler = SigChldHandler;
        ......
    }
    // This signal handler is for zygote mode, since the zygote must reap its children
    static void SigChldHandler(int /*signal_number*/) {
        pid_t pid;
        ......
        while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
            ......
            if (pid == gSystemServerPid) {
                ALOGE("Exit zygote because system server (%d) has terminated", pid);
                kill(getpid(), SIGKILL);
            }
        }
        ......
    }
    複製代碼
  • SystemServer進程fork後,在fork出的子進程中:
    • 先關閉從Zygote進程繼承來的socket
    • 接着調用handleSystemServerProcess()來初始化SystemServer進程,流程以下:
    /** * Finish remaining work for the newly forked system server process. */
    private static Runnable handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) {
        // set umask to 0077 so new files and directories will default to owner-only permissions.
        // 設置umask爲0077(權限的補碼)
        // 這樣SystemServer建立的文件屬性就是0700,只有進程自己能夠訪問
        Os.umask(S_IRWXG | S_IRWXO);
        ......
        if (parsedArgs.invokeWith != null) {
            // 通常狀況不會走到這裏,invokeWith基本上都爲null
            ......
            // 經過 app_process 來啓動,進程不會return
            WrapperInit.execApplication(parsedArgs.invokeWith,
                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
                    VMRuntime.getCurrentInstructionSet(), null, args);
    
            throw new IllegalStateException("Unexpected return from WrapperInit.execApplication");
        } else {
            ......
            // 經過查找啓動類的main方法,而後打包成Runnable對象返回
            return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
        }
        /* should never reach here */
    }
    複製代碼
    • invokeWith一般爲null,因此基本都是經過ZygoteInit.zygoteInit()函數來處理,返回打包好的Runnable對象。後面就是執行r.run()來調用SystemServermain()方法了

咱們看看SystemServermain()方法幹了啥socket

SystemServer初始化

SystemServermain()方法以下:ide

public static void main(String[] args) {
        new SystemServer().run();
    }
複製代碼

main()方法中建立了一個SystemServer的對象並調用run()函數,函數代碼以下,刪減版:

private void run() {
        // System Server 啓動前的準備階段
        try {
            // 設置系統時間
            if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {
                Slog.w(TAG, "System clock is before 1970; setting to 1970.");
                SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);
            }
            ...... // 省略時區,語言、國家的設置
            ...... // 省略 Binder、Sqlite相關屬性設置
            // Here we go!
            Slog.i(TAG, "Entered the Android system server!");
            int uptimeMillis = (int) SystemClock.elapsedRealtime();
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, uptimeMillis);
            ......
            // 設置當前虛擬機的運行庫路徑
            SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary());
            // 調整內存
            VMRuntime.getRuntime().clearGrowthLimit();
            VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
            ...... //省略一些系統屬性的設置
            // 設置進程相關屬性
            android.os.Process.setThreadPriority(
                android.os.Process.THREAD_PRIORITY_FOREGROUND);
            android.os.Process.setCanSelfBackground(false);
            // 初始化Looper相關
            Looper.prepareMainLooper();
            Looper.getMainLooper().setSlowLogThresholdMs(SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
            // 加載libandroid_services.so
            System.loadLibrary("android_servers");
            .....
            // Initialize the system context.
            createSystemContext();
            // Create the system service manager.
            mSystemServiceManager = new SystemServiceManager(mSystemContext);
            mSystemServiceManager.setStartInfo(mRuntimeRestart,mRuntimeStartElapsedTime, mRuntimeStartUptime);
            LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
            ......
        } finally {
            traceEnd();  // InitBeforeStartServices
        }
        // System Server 的啓動階段
        try {
            traceBeginAndSlog("StartServices");
            startBootstrapServices();
            startCoreServices();
            startOtherServices();
            ......
        } catch (Throwable ex) {
            ......
            throw ex;
        } finally {
            traceEnd();
        }
        ......
        // Loop forever.
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
複製代碼

run()方法主要分爲兩個階段:

  • 一個是SystemServer啓動前的準備階段,主要進行了:

    • 系統時間、時區、語言的設置
    • 虛擬機運行庫、內存參數、內存利用率的調整
    • 設置當前線程的優先級
    • 加載libandroid_services.so
    • 初始化Looper
    • SystemContextSystemServiceManager的初始化
  • 另外一個是SystemServer的啓動階段:

    • 執行startBootstrapServicesstartCoreServicesstartOtherServices啓動全部的系統服務
    • 調用Looper.loop()循環處理消息

咱們先來看下準備階段SystemContext的初始化過程

createSystemContext()初始化

函數以下:

private void createSystemContext() {
        ActivityThread activityThread = ActivityThread.systemMain();
        mSystemContext = activityThread.getSystemContext();
        mSystemContext.setTheme(DEFAULT_SYSTEM_THEME);
        
        final Context systemUiContext = activityThread.getSystemUiContext();
        systemUiContext.setTheme(DEFAULT_SYSTEM_THEME);
    }
複製代碼

createSystemContext()方法的流程比較簡單:

  • 經過ActivityThread的靜態方法systemMain()來獲得一個ActivityThread對象
  • 而後調用ActivityThread對象的getSystemContext()/getSystemUiContext()方法來獲得對應的Context對象
  • 經過Context對象設置一個默認的theme

因此重點仍是ActivityThread.systemMain()方法:

public static ActivityThread systemMain() {
        // The system process on low-memory devices do not get to use hardware
        // accelerated drawing, since this can add too much overhead to the
        // process.
        if (!ActivityManager.isHighEndGfx()) {
            ThreadedRenderer.disable(true);
        } else {
            ThreadedRenderer.enableForegroundTrimming();
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(true, 0);
        return thread;
    }
複製代碼

方法內容並不複雜,首先判斷是否須要啓動硬件渲染,而後建立了一個ActivityThread對象。

關於ActivityThread,在Zygote進程學習的時候咱們已經知道:

  • ActivityThread是應用程序的主線程類
  • 啓動應用最後執行到的就是ActivityThreadmain()方法

關鍵是SystemServer爲何要建立ActivityThread對象呢?
實際上SystemServer不只僅是一個單純的後臺進程,它也是一個運行着組件Service的進程,不少系統對話框就是從SystemServer中顯示出來的,所以SystemServer自己也須要一個和APK應用相似的上下文環境,建立ActivityThread是獲取這個環境的第一步

可是ActivityThreadSystemServer進程中與普通進程仍是有區別的,這裏主要是經過attach(boolen system,int seq)方法的參數system來標識,函數以下:

// system = true 表示這是在 SystemServer 中建立的
    private void attach(boolean system, long startSeq) {
        sCurrentActivityThread = this;
        mSystemThread = system;
        if (!system) {
            // 正常應用的啓動纔會走到這裏
            ......
        } else {
            // Don't set application object here -- if the system crashes,
            // we can't display an alert, we just want to die die die.
            // 設置在DDMS中的應用名稱
            android.ddm.DdmHandleAppName.setAppName("system_process",UserHandle.myUserId());
            try {
                // 建立Instrumentation對象
                // 一個ActivityThread對應一個,能夠監視應用的生命週期
                mInstrumentation = new Instrumentation();
                mInstrumentation.basicInit(this);
                // 建立Context對象
                ContextImpl context = ContextImpl.createAppContext(
                        this, getSystemContext().mPackageInfo);
                mInitialApplication = context.mPackageInfo.makeApplication(true, null);
                mInitialApplication.onCreate();
            } catch (Exception e) {
                throw new RuntimeException(
                        "Unable to instantiate Application():" + e.toString(), e);
            }
        }
複製代碼

attach()方法在systemtrue的狀況下:

  • 設置進程在DDMS中的名稱
  • 建立了ContextImplApplication對象
  • 最後調用了Application對象的onCreate方法

這裏在模仿應用啓動有木有,是哪個APK呢?
ContextImpl對象建立時,使用的是getSystemContext().mPackageInfo來獲取的Apk信息,咱們看看:

public ContextImpl getSystemContext() {
        synchronized (this) {
            if (mSystemContext == null) {
                mSystemContext = ContextImpl.createSystemContext(this);
            }
            return mSystemContext;
        }
    }
複製代碼

再看下createSystemContext()方法:

static ContextImpl createSystemContext(ActivityThread mainThread) {
        LoadedApk packageInfo = new LoadedApk(mainThread);
        ......
        return context;
    }
複製代碼

createSystemContext()方法建立了一個LoadedApk的對象,也就是mPackageInfo,咱們來看看它只有一個參數的構造方法:

LoadedApk(ActivityThread activityThread) {
        mActivityThread = activityThread;
        mApplicationInfo = new ApplicationInfo();
        mApplicationInfo.packageName = "android";
        mPackageName = "android"; // 設置包名爲 android
        ......
    }
複製代碼

LoadedApk對象用來保存一個已經加載了的apk文件的信息,上面的構造方法將使用的包名指定爲android

還記得Android資源管理篇framewok-res.apk麼,它的包名就是android。。。

getSystemContext().mPackageInfo指明的是framwork-res.apk的信息

還要記得,framwork-res.apk的相關資源 在Zygote進程中的preloading環節就已經加載完成了喲

因此,createSystemContext()方法的初始化至關於建立了一個framwork-res.apk的上下文環境,而後再進行相應的Theme設置

而對於getSystemUiContext()getSystemContext()這兩個方法,咱們查看ActivityThread的成員變量就會發現分別對應了mSystemUiContextmSystemContext。這部分在5.0的源碼中還未出現,應該是Android後面進行了優化拆分,暫不追究,哈哈哈

到這裏,SystemServer準備階段的工做已經完成,後面就是啓動一系列的Service

SystemServer啓動階段

啓動階段分紅了三步:

startBootstrapServices();
startCoreServices();
startOtherServices();
複製代碼

startBootstrapServices()

startBootstrapServices()啓動的都是一些很基礎關鍵的服務,這些服務都有很複雜的相互依賴性,咱們來簡單看下:

private void startBootstrapServices() {
        ......
        // Installer 會經過binder關聯 installd 服務
        // installd 在Init進程中就會被啓動,很重要,因此放在第一位
        // installd 支持不少指令,像ping、rename都是在其中定義的
        // 後面在APK安裝篇幅單獨介紹
        Installer installer = mSystemServiceManager.startService(Installer.class);
        traceEnd();
        ......
        // 添加設備標識符訪問策略服務
        mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class);
        ......
        // 啓動 ActivityManagerService,並進行一些關聯操做
        mActivityManagerService = mSystemServiceManager.startService(
                ActivityManagerService.Lifecycle.class).getService();
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
        mActivityManagerService.setInstaller(installer);
        
        // 啓動 PowerManagerService
        mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
        ......
        // 初始化mActivityManagerServicede的電源管理相關功能
        mActivityManagerService.initPowerManagement();
        ......
        // 啓動 RecoverySystemService
        // recovery system也是很重要的一個服務
        // 能夠觸發ota,設置或清除bootloader相關的數據
        mSystemServiceManager.startService(RecoverySystemService.class);
        ......
        // 標記啓動事件
        RescueParty.noteBoot(mSystemContext);
        // 啓動LightsService,管理LED、背光顯示等
        mSystemServiceManager.startService(LightsService.class);
        // 通知全部服務當前狀態
        mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
        ......// 省略一些看上去不重要的service
        // 啓動PackageManagerService
        mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
        mFirstBoot = mPackageManagerService.isFirstBoot();
        mPackageManager = mSystemContext.getPackageManager();
        ......
        // 啓動 OverlayManagerService
        OverlayManagerService overlayManagerService = new OverlayManagerService(
                mSystemContext, installer);
        mSystemServiceManager.startService(overlayManagerService);
        ......
        // 啓動 SensorService 這是一個native方法
        // SensorService 提供的是各類傳感器的服務
        startSensorService();
    }
複製代碼

單單startBootstrapServices啓動的服務就不止10種,咱們先總結上面的代碼規律。

能夠發現,服務的啓動基本上都是經過SystemServiceManagerstartService()方法。跟蹤代碼咱們會找到SystemServiceManager的一個成員變量ArrayList<SystemService> mServices

SystemServiceManager管理的其實就是SystemService的集合,SystemService是一個抽象類

若是咱們想定義一個系統服務,就須要實現SystemService這個抽象類

PackageManagerService有點特殊,不過這並不影響這種設計模式,後面的章節會單獨學習到它的獨特之處

startCoreServices()

咱們接着看下startCoreServices(),相對簡潔了不少:

private void startCoreServices() {
        // Tracks the battery level. Requires LightService.
        // 啓動電池管理service,依賴bootstrap中的LightService
        // 該服務會按期廣播電池的相關狀態
        mSystemServiceManager.startService(BatteryService.class);

        // 啓動 應用使用狀況數據收集服務
        mSystemServiceManager.startService(UsageStatsService.class);
        // ActivityManagerService 又來橫插一腳,無處不在的傢伙
        mActivityManagerService setUsageStatsManager( LocalServices.getService(UsageStatsManagerInternal.class));
        ......
        // 啓動WebView service
        mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
        ......
        // 啓動 binder 調用的耗時統計服務
        BinderCallsStatsService.start();
    }
複製代碼

startCoreServices()啓動的服務看上去也沒那麼核心不是。

startOtherServices()

再看下startOtherServices()足足有1000多行,咱們找下重點

/** * Starts a miscellaneous grab bag of stuff that has yet to be refactored * and organized. */
    // google 形容這部分是混亂的,有待整理重構,確實噁心啊
    // 不少服務會依賴其餘的服務,好比NotificationManager會依賴StorageManager......
    // 讓咱們來精簡下
    private void startOtherServices() {
        ......// 初始化一些比較基礎的服務
        // WindowManagerService、NetworkManagementService
        // WatchDog、NetworkPolicyManagerService等
        
        .....// 初始化 UI 相關的服務
        // InputMethodManagerService、AccessibilityManagerService
        // StorageManagerService、NotificationManagerService
        // UiModeManagerService等
        
        ......// 此處省略了約800行的各式各樣的服務的初始化
        // 有點心疼當時寫這個方法的前輩,預祝聖誕快樂。。。。

        // These are needed to propagate to the runnable below.
        // 將上面初始化好的一些必要的服務在ActivityManagerService的systemReady中進行進一步的處理
        // 須要進一步處理的服務就是下面這些
        final NetworkManagementService networkManagementF = networkManagement;
        .....
        final IpSecService ipSecServiceF = ipSecService;
        final WindowManagerService windowManagerF = wm;
        // 執行 ActivityManagerService 的 systemReady 方法
        mActivityManagerService.systemReady(() -> {
            ......
            // 標記狀態
            mSystemServiceManager.startBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
            ...... 
            startSystemUi(context, windowManagerF);
            ......// 對前面建立好的服務作進一步的配置,大可能是執行一些systemReady操做,如:
            // networkManagementF.systemReady()、ipSecServiceF.systemReady()、networkStatsF.systemReady()
            // connectivityF.systemReady()、networkPolicyF.systemReady(networkPolicyInitReadySignal)
            Watchdog.getInstance().start();
            // Wait for all packages to be prepared
            mPackageManagerService.waitForAppDataPrepared();
            ......
            // 通知全部服務當前狀態
            mSystemServiceManager.startBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
            ......// 嘗試初次執行一些服務,像定位、地區檢測等
            // locationF.systemRunning()、countryDetectorF.systemRunning()、networkTimeUpdaterF.systemRunning()
            ......
        }, BOOT_TIMINGS_TRACE_LOG);
    }
複製代碼

註釋很簡潔詳細了哈!

啓動階段的總結

SystemServer的啓動階段咱們能夠了解:

  • SystemServer啓動的服務是真的多,並且大多數服務之間存在較強的依賴,有着相對嚴格的啓動順序
  • 系統服務須要實現com.android.server.SystemService(直接繼承或者提供相應的內部類),InstallerAppWidgetService分別表明了兩種實現方式
    • grep查看相應的繼承類,足足有86種之多
  • 系統服務的啓動經過SystemServiceManagerstartService(systemservice)函數
    • startService(systemservice)函數會把要啓動的systemservice添加到mServices集合中
    • 而後會回調SystemServiceonStart抽象方法啓動服務
  • SystemServer的啓動階段以mActivityManagerService.systemReady()方法爲終點,這個函數比較吸引人的是:
    • 傳入了一個Runnable對象,對一些服務進行了額外的處理,這個對象主要是用來:
      • 啓動SystemUIWatchdog等服務
      • 執行一些服務的systemReady()systemRunning()方法
        • 再吐槽一下,這兩個函數徹底沒有抽象出來,都是在各個服務中單獨聲明,很難去閱讀
    • 執行一個很著名的函數startHomeActivityLocked()來廣播通知launcher啓動

好了,適可而止了,本篇是爲了梳理SystemServer的啓動流程,Launcher啓動什麼的留在後面詳細學習哈!

接下來看看WatchDog相關的知識

SystemServer中的Watchdog

Init進程的學習中咱們介紹了watchdogd守護進程,這個守護進程會在規定的時間內向硬件看門狗發送消息表示本身沒出故障;超時看門狗就會重啓設備。

對於SystemServer進程來講,運行的服務數量超過80種,是最有可能出現問題的進程,所以,有必要對SystemServer中運行的各類線程實施監控。

可是,若是仿照硬件看門狗的方式每一個線程定時去喂狗,不但很是浪費資源,並且會致使程序設計更加複雜。所以,Android開發了Watchdog類做爲軟件看門狗來監控SystemServer中的線程。一旦發現問題,Watchdog會殺死SystemServer進程。

前面在Zygote進程部分的學習咱們知道:

  • SystemServer的父進程Zygote進程接收到SystemServer的死亡信號後會殺死本身。
  • 而後Zygote進程的死亡信號會傳遞給Init進程,Init進程會殺死Zygote進程的全部子進程並重啓Zygote進程。

這樣,整個系統的基礎服務都會重啓一遍。這種軟啓動的方式能解決大部分問題,而且速度更快。那麼讓咱們來學習下怎麼實現的吧

啓動Watchdog

Watchdog是繼承自Thread,在SystemServer中建立Watchdog對象是在startOtherServices()中,代碼以下:

final Watchdog watchdog = Watchdog.getInstance();
        watchdog.init(context, mActivityManagerService);
複製代碼

Watchdog是單例的運行模式,第一次調用getInstance()會建立Watchdog對象並保存到全局變量sWatchdog中,咱們看下構造方法:

private Watchdog() {
        // 用來監聽服務的
        mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
        "foreground thread", DEFAULT_TIMEOUT);
        mHandlerCheckers.add(mMonitorChecker);
        mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
        "main thread", DEFAULT_TIMEOUT));
        mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
                "ui thread", DEFAULT_TIMEOUT));
        mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
                "i/o thread", DEFAULT_TIMEOUT));
        mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
                "display thread", DEFAULT_TIMEOUT));
        addMonitor(new BinderThreadMonitor());
        mOpenFdMonitor = OpenFdMonitor.create();
    }
複製代碼
  • Watchdog構造方法的主要工做是建立幾個HandlerChecker對象,並添加到mHandlerCheckers集合中。
    • 每一個HandlerChecker對象對應一個被監控的HandlerThread線程,經過獲取到對應線程的Handler來與其通訊。
    • HandlerChecker類的簡要結構以下:
    public final class HandlerChecker implements Runnable {
        private final Handler mHandler;
        private final String mName;
        private final long mWaitMax;
        private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
        ......
        HandlerChecker(Handler handler, String name, long waitMaxMillis) {
            mHandler = handler;
            mName = name;
            mWaitMax = waitMaxMillis;
            ......
        }
        public void addMonitor(Monitor monitor) {
            mMonitors.add(monitor);
        }
    }
    public interface Monitor {
        void monitor();
    }
    複製代碼
    • 請留意Monitor接口,若是一個服務須要經過Watchdog來監控,它必須實現這個Monitor接口
  • 構造方法中的FgThreadUiThreadIoThreadDisplayThread都是繼承自HandlerThread
    • Android用它們分別執行不一樣類型的任務,Io相關UI相關
    • 實現上都是經過Handler相關的那一套,你們看看HandlerThread類就知道了。

Watchdog對象建立後,接下來會調用init()方法進行初始化,代碼以下:

public void init(Context context, ActivityManagerService activity) {
        mResolver = context.getContentResolver();
        mActivity = activity;
        context.registerReceiver(new RebootRequestReceiver(),
                new IntentFilter(Intent.ACTION_REBOOT),
                android.Manifest.permission.REBOOT, null);
    }
複製代碼

init()註冊了廣播接收器,當收到Intent.ACTION_REBOOT的廣播後,會執行重啓操做。

最後,在startOtherServices()mActivityManagerService.systemReady()階段,執行Watchdog的啓動:

mActivityManagerService.systemReady(() -> {
        ...
        Watchdog.getInstance().start();
        ...
},...);
複製代碼

Watchdog監控的服務和線程

Watchdog主要監控線程,在SystemServer進程中運行着不少線程,它們負責處理一些重要模塊的消息。若是一個線程陷入死循環或者和其餘線程相互死鎖了,Watchdog須要有辦法識別出它們。

Watchdog中提供了兩個方法addThread()addMonitor()分別用來增長鬚要監控的線程和服務:

  • addThread()用來添加線程監聽:

    public void addThread(Handler thread, long timeoutMillis) {
        synchronized (this) {
            if (isAlive()) {
                throw new RuntimeException("Threads can't be added once the Watchdog is running");
            }
            final String name = thread.getLooper().getThread().getName();
            mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis));
        }
    }
    複製代碼
    • 建立了一個HandlerChecker對象並添加到mHandlerCheckers集合中
  • addMonitor()用來添加服務的監聽:

    public void addMonitor(Monitor monitor) {
        synchronized (this) {
            if (isAlive()) {
                throw new RuntimeException("Monitors can't be added once the Watchdog is running");
            }
            mMonitorChecker.addMonitor(monitor);
        }
    }
    複製代碼
    • 對服務的監控Android使用mMonitorChecker一個HandlerChecker對象來完成
    • mMonitorCheckerWatchdog對象初始化時就建立完成

經過addThread()添加監控的線程有:

  • 主線程
  • FgThread
  • UiThread
  • IoThread
  • DisplayThread
  • PowerManagerService的線程
  • PackageManagerService的線程
  • PermissionManagerService的線程
  • ActivityManagerService的線程

經過addMonitor()添加監控的服務有:

NetworkManagementService.java
StorageManagerService.java
ActivityManagerService.java
InputManagerService.java
MediaRouterService.java
MediaSessionService.java
MediaProjectionManagerService.java
PowerManagerService.java
TvRemoteService.java
WindowManagerService.java
複製代碼

Watchdog監控的原理

前面講過,Binder調用是在某個Binder線程中執行的,可是執行的線程並不固定,所以,Watchdog不能用監控一個普通線程的方法來判斷某個Binder服務是否正常

若是運行在Binder線程中的方法使用了全局的資源,就必須創建臨界區來實施保護。一般的作法是使用synchronized關鍵字。如:

synchronized (mLock){
    ......
}
複製代碼

這種狀況下,咱們可使用鎖mLock持有的時間是否超時來判斷服務是否正常。

Watchdog的思想就是:給線程發送消息,若是發送的消息不能在規定的時間內獲得處理,即代表線程被不正常的佔用了。

咱們看下Watchdog總體流程的實現:

static final long DEFAULT_TIMEOUT = DB ? 10*1000 : 60*1000;
    static final long CHECK_INTERVAL = DEFAULT_TIMEOUT / 2;
    @Override
    public void run() {
        boolean waitedHalf = false;
        while (true) {
            final List<HandlerChecker> blockedCheckers;
            final String subject;
            final boolean allowRestart;
            int debuggerWasConnected = 0;
            synchronized (this) {
                long timeout = CHECK_INTERVAL;
                // 經過scheduleCheckLocked給監控的線程發送消息
                for (int i=0; i<mHandlerCheckers.size(); i++) {
                    HandlerChecker hc = mHandlerCheckers.get(i);
                    hc.scheduleCheckLocked();
                }
                ......
                // 休眠特定的時間,默認爲30s
                long start = SystemClock.uptimeMillis();
                while (timeout > 0) {
                    ......
                    try {
                        wait(timeout);
                    } catch (InterruptedException e) {
                        Log.wtf(TAG, e);
                    }
                    ......
                    timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
                }
                ......
                // 檢測是否有線程或服務出問題了
                final int waitState = evaluateCheckerCompletionLocked();
                    if (waitState == COMPLETED) {
                        // The monitors have returned; reset
                        waitedHalf = false;
                        continue;
                    } else if (waitState == WAITING) {
                        // still waiting but within their configured intervals; back off and recheck
                        continue;
                    } else if (waitState == WAITED_HALF) {
                        if (!waitedHalf) {
                            // We've waited half the deadlock-detection interval. Pull a stack
                            // trace and wait another half.
                            ArrayList<Integer> pids = new ArrayList<Integer>();
                            pids.add(Process.myPid());
                            ActivityManagerService.dumpStackTraces(true, pids, null, null,
                                getInterestingNativePids());
                            waitedHalf = true;
                        }
                        continue;
                    }
                ......

            // If we got here, that means that the system is most likely hung.
            // First collect stack traces from all threads of the system process.
            // Then kill this process so that the system will restart.
            ......
            {
                Process.killProcess(Process.myPid());
                System.exit(10);
            }
            waitedHalf = false;
        }
    }
複製代碼

run()方法中是一個無限循環,每次循環中主要進行:

  • 調用HandlerCheckerscheduleCheckLocked方法給全部受監控的線程發送消息,代碼以下:
    public void scheduleCheckLocked() {
            if (mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling()) {
                mCompleted = true;
                return;
            }
            if (!mCompleted) {
                // we already have a check in flight, so no need
                return;
            }
            mCompleted = false;
            mCurrentMonitor = null;
            mStartTime = SystemClock.uptimeMillis();
            mHandler.postAtFrontOfQueue(this);
        }
    複製代碼
    • HandlerChecker對象先判斷mMonitorssize是否爲0
      • 0說明當前HandlerChecker對象沒有監控服務
      • 若是此時被監控線程的消息隊列處於空閒狀態,說明線程運行良好,直接返回
    • 不爲0:
      • 則先把mCompleted設爲false,而後記錄消息發送時間mStartTime
      • 而後調用postAtFrontOfQueue給被監控線程發送一個Runnable消息
  • postAtFrontOfQueue()發送的消息的處理方法就是HandlerCheckerrun()方法:
    @Override
        public void run() {
            final int size = mMonitors.size();
            for (int i = 0 ; i < size ; i++) {
                synchronized (Watchdog.this) {
                    mCurrentMonitor = mMonitors.get(i);
                }
                mCurrentMonitor.monitor();
            }
            synchronized (Watchdog.this) {
                mCompleted = true;
                mCurrentMonitor = null;
            }
        }
    複製代碼
    • 若是消息處理方法run()能執行,說明受監控的線程自己沒有問題
    • 而後經過調用服務中實現的monitor()方法檢查被監控服務的狀態
      • 一般monitor()方法的實現是獲取服務中的鎖,若是不能獲得,線程就會掛起,monitor()一般實現以下:
      public void monitor(){
          sychronized(mLock){}
      }
      複製代碼
      • 這樣mCompleted沒法被設置爲true
    • mCompleted設置爲true,說明HandlerChecker對象監控的線程或者服務正常。不然就可能有問題。
      • 是否真的有問題,須要根據等待的時間是否超過規定時間來判斷
  • 給受監控的線程發送完消息後,調用wait方法讓Watchdog休眠特定的一段時間,默認爲30S
  • 休眠結束後,經過evaluateCheckerCompletionLocked()逐個檢查是否有線程或服務出現問題,一旦發現問題,立刻殺死進程。方法代碼以下:
    private int evaluateCheckerCompletionLocked() {
        int state = COMPLETED;
        for (int i=0; i<mHandlerCheckers.size(); i++) {
            HandlerChecker hc = mHandlerCheckers.get(i);
            state = Math.max(state, hc.getCompletionStateLocked());
        }
        return state;
    }
    複製代碼
    • evaluateCheckerCompletionLocked()調用每一個HandlerCheckergetCompletionStateLocked()方法來獲取對象的狀態值,getCompletionStateLocked代碼以下:

      public int getCompletionStateLocked() {
          if (mCompleted) {
              return COMPLETED;
          } else {
              long latency = SystemClock.uptimeMillis() - mStartTime;
              if (latency < mWaitMax/2) {
                  return WAITING;
              } else if (latency < mWaitMax) {
                  return WAITED_HALF;
              }
          }
          return OVERDUE;
      }
      複製代碼
    • 狀態值分爲4種:

      • COMPLETED:值爲0,表示狀態良好
      • WAITING:值爲1,表示正在等待消息處理結果
      • WAITED_HALF:值爲2,表示正在等待而且等待的時間已經大於規定時間的一半,可是還未超過規定時間
      • OVERDUE:值爲3,表示等待時間已經超過了規定的時間
    • evaluateCheckerCompletionLocked()但願獲取到最壞的狀況,因此使用Math.max()來進行比對過濾

結語

SystemServer自己沒有特別難理解的地方,比較費神的是它維持了太多的服務,start*Services()閱讀起來太痛苦了,好在:

  • 在服務列表中看到了不少眼熟的服務,像ActivityManagerServicePackageManagerService等服務,不至於特別枯燥,讓後續的學習有了些盼頭

  • 關於Watchdog的學習上,基於HandlerThread構建的監聽模式很值得學習

下一篇開始學習PackageManagerService相關的知識,go go go!

相關文章
相關標籤/搜索