Android系統啟動分析(Init->Zygote->SystemServer->Home activity)


整個Android系統的啟動分為Linux Kernel的啟動和Android系統的啟動。Linux Kernel啟動起來后,然后運行第一個用戶程序,在Android中就是init程序。

-------------------------------------------------

以下的內容應該算是學習筆記,特地整理成文。

-------------------------------------------------

1 init程序

init是linux系統中用戶空間的第一個進程。由於Android是基於linux內核的,所以init也是Android系統中用戶空間的第一個進程,它的進程號是1。init程序並不是由一個源文件組成的,而是由一組源代碼文件的目標文件鏈接而成,這些文件位於如下目錄:

<android source code directory>/system/core/init/

它的主要職責在於:

1)、掛載目錄,比如/sys, /dev, /proc

mkdir("/dev", 0755);
mkdir("/proc", 0755);
mkdir("/sys", 0755);
......

2)、初始化屬性,提供property service(屬性服務)管理Android系統中的屬性

3)、處理配置文件命令(主要是init.rc腳本文件)

4)、性能分析和執行其它進程

2.1 init分析

init進程的入口函數是main,主要做了這些工作:

1)、解析配置文件,主要是系統配置文件init.rc和與硬件平台相關的配置文件(如init.xxx.rc),在此階段,也會解析service。

init.rc文件包含五個類型的聲明:

  • Actions(動作以命令流程命名,有一個觸發器決定動作是否發生)
  • on <trigger>
            <command>
            <command>
  • Commands
  • class_start <serviceclass>
        Start all services of the specified class if they are not already running
  • Services(是init進程啟動的程序,以及當服務退出時init進程會視情況重啟服務)
  • Options(選項是對服務的描述,它們影響init進程如何以及何時啟動服務)
  • service <name> <pathname> [ <argument> ]*
            <option>
            <option>
  • Imports
  • import <path>
        Parse an init config file, extending the current configuration.
Action Service 描述
on early-init 設置init進程以及它創建的子進程的優先級,設置init進程的安全環境
on init 設置全局環境,為cpu accounting創建cgroup(資源控制)掛載點
on fs 掛載mtd分區
on post-fs 改變系統目錄的訪問權限
on post-fs-data 改變/data目錄以及它的子目錄的訪問權限
on boot 基本網絡的初始化,內存管理等等
service servicemanager 啟動系統管理器管理所有的本地服務,比如位置、音頻、Shared preference等等
service zygote 啟動zygote作為應用進程

2)、執行各個階段的動作,創建zygote的工作就是在其中某個階段完成。

3)、調用property_init初始化屬性相關的資源,並且通過property_start_service啟動屬性服務。

4)、init進入一個無限循環,然后等待響應。

2.2 屬性服務

應用程序可以通過這個屬性機制,查詢或設置屬性。這個屬性服務是怎么實現的呢?其中與init.c和屬性服務有關的代碼:

property_init();
property_set_fd = start_property_service();

2.2.1 屬性服務初始化

首先創建存儲空間,property_service.c中有property_init函數

void property_init(void){
    init_property_area(); // 初始化屬性存儲區域
    // 加載default.prop文件
    load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
}

雖然屬性區域是由init進程創建的,但Android系統希望其它進程也能讀取這塊內存里的東西。為了做到這一點,它做了以下的工作:

1)、把屬性區域創建在共享內存上,而共享內存是可以跨進程的。

2)、Android利用gcc的constructor屬性,指明了一個__libc_prenit函數,當bionic libc庫被加載時,將自動調用這個__libc_preni,這個函數內部就將完成共享內存到本地進程的映射工作。這樣一來,其它進程就知道了這個共享內存。

接着,客戶端進程獲取存儲空間。

2.2.2 屬性服務器的分析

2.2.2.1 啟動屬性服務器

init進程會啟動一個屬性服務器,而客戶端只能通過與屬性服務器交互來設置屬性。

2.2.2.2 處理設置屬性請求

接受請求的地方在init進程中,當屬性服務器收到客戶端請求時,init會調用handle_property_set_fd進行處理。

if(ufds[1].revents == POLLIN){
    handle_property_set_fd(property_set_fd);
}

當客戶端的權限滿足要求時,init就調用property_set進行相關處理。

if(check_perms(msg.name, cr.uid, cr.gid)){
    property_set((char *) msg.name, (char *) msg.value);
}
2.2.2.3 客戶端發送請求

客戶端通過property_set發送請求,property_set由libcutils庫提供。

int property_set(const chatr *key, const char *value){
    prop_msg msg;
    unsigned resp;
    // other code
    ......
    msg.cmd = PROP_MSG_SETPROP; // 設置消息碼
    strcpy((char *) msg.name, key);
    strcpy((char *) msg.value, value);
    // 發送請求
    return send_prop_msg(&msg);
}
static int send_prop_msg(prop_msg *msg){
    int s;
    int r;
    // 建立和屬性服務器的socket鏈接
    s = socket_local_client(PROP_SERVICE_NAME, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
    if(s < 0){
        return -1;
    }
    // 通過socket發送
    while((r = send(s, msg, sizeof(prop_msg), 0)) < 0){
        if((errno == EINTR) || (errno == EAGAIN)){
            continue;
        }
        break;
    }

    if(r == sizeof(prop_msg)){
        r = 0;
    } else {
        r = -1;
    }

    close(s);
    return r;
}

2 zygote分析

zygote本身是一個Native的應用程序,與驅動、內核等無關。它是由init進程根據init.rc文件中的配置創建的。zygote最初的名字叫“app_process”,這個名字是在Android.mk文件中指定的。在運行中,app_process通過linux下的pctrl系統調用將自己的名字換成了zygote。

在Java中,我們知道不同的虛擬機實例會為不同的應用分配不同的內存。假如Android應用應該盡可能快地啟動,但如果Android系統為每一個應用啟動不同的Dalvik虛擬機實例,就會消耗大量的內存以及時間。因此,為了克服這個問題,Android系統創造了”zygote”。zygote讓Dalvik虛擬機共享代碼、低內存占用以及最小的啟動時間成為可能。

zygote進程由init通過fork而來,在init.rc中設置的啟動參數如下:

-Xzygote /system/bin --zygote --start-system-server

zygote的原型app_process對應的源文件是App_main.cpp。

int main(int argc, const char* const argv[]){
    ......
    AppRuntime runtime;
    ......
    int i = runtime.addVmArguments(argc, argv);
    if(i < argc){
        runtime.mParentDir = argv[i++];
    }

    if(i < argc){
        arg = argv[i++];
        if(0 == strcmp("--zygote", arg)){
            bool startSystemServer = (i < argc) ? strcmp(argv[i], "--start-system-server") == 0: false;
            setArgv0(argv0, "zygote");
            set_process_name("zygote"); // 設置進程名
            runtime.start("com.android.internal.os.ZygoteInit", startSystemServer);
        }
        ......
    }
    ......
}

2.1 AppRuntime分析

AppRuntime從AndroidRuntime類派生,它的聲明和實現在App_main.cpp中。AppRuntime重載了onStarted, onZygoteInit和onExit函數。其中AndroidRuntime::start(const char* className, const bool startSystemServer)函數做了幾件事情:

2.1.1 創建虛擬機--startVm

int AndroidRuntime::startVm(JavaVm** pJavaVM, JNIEnv** pEnv){
    // JNI check是指Native層調用JNI函數時,系統所做的一些檢查工作。例如,調用NewUTFString函數時,系統會檢查傳入的字符串是不是符合UTF-8的要求。
    property_get("dalvik.vm.checkjni", propBuf, "");
    ......
    // 設置虛擬機的heapsize,默認為16MB
    strcpy(heapsizeOptsBuf, "-Xmx");
    property_get("dalvik.vm.heapsize", heapsizeOptsBuf + 4, "16m");
    ......
    if(checkJni){
        opt.optionsString = "-Xcheck:jni";
        mOptions.add(opt);
        // JNI check中的資源檢查,系統中創建的Global Reference個數不能超過2000
        opt.optionString = "-Xjnigreflimit:2000";
        mOptions.add(opt);
    }

    // 調用JNI_CreateJavaVM創建虛擬機,pEnv返回當前線程的JNIEnv變量
    if(JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0){
        goto bail;
    }

    return 0;

bail:
    free(stackTraceFile);
    return result;
}

2.1.2 注冊JNI函數--startReg

int AndroidRuntime::startReg(JNIEnv* env){
    ......
    if(register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0){
        env->PopLocalFrame(NULL);
        return -1;
    }
    env->PopLocalFrame(NULL);
    return 0;
}
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env){
    for(size_t i = 0; i < count; i++){
        if(array[i].mProc(env) < 0){
            return -1;
        }
    }
    return 0;
}
statci const RegJNIRec gRegJNI[] = {
    REG_JNI(register_android_debug_JNITest),
    REG_JNI(register_com_android_internal_os_RuntimeInit),
    REG_JNI(register_android_os_SystemClock),
    ......
}

上面的mProc就是為Java類注冊JNI函數。

2.1.3 通過JNI調用Java函數,進入Java世界

env->CallStaticVoidMethod(startClass, startMeth, strArray);

CallStaticVoidMethod最終將調用com.android.internal.os.ZygoteInit的main函數。

public static void main(String argv[]){
    try{
        SamplingProfilerIntegration.start();
        // 1. 注冊zygote用的socket. 基於AF_UNIX類型,是一個本機socket
        registerZygoteSocket();
        // 2. 預加載類和資源
        preloadClasses();
        preloadResources();
        ......
        // 強制執行一次垃圾回收
        gc();

        if(argv[1].equals("true")){
            startSystemServer(); // 3. 啟動system_server進程。該進程是framework的核心。
        } else if(!argv[1].equals("false")){
            throw new RuntimeException(argv[0] + USAGE_STRING);
        }

        if(ZYGOTE_FORK_MODE){
            runForkMode();
        } else {
            runSelectLoopMode(); // 4. zygote調用這個函數,進入等待喚醒的狀態
        }
        closeServerSocket();
    } catch(MethodAndArgsCaller caller){
        caller.run();
    } catch(RuntimeException ex){
        closeServerSocket();
        throw ex;
    }
    ......
}

3 SystemServer分析

SystemServer是由Zygote通過Zygote.forkSystemServer函數fork出來的。

pid = Zygote.forkSystemServer();
if(pid == 0){ // 子進程返回0,即systemServer
    handleSystemServerProcess(parseArgs);
}

在handleSystemServerProcess函數中,首先會關閉從Zygote繼承下來的socket,並設置SystemServer進程的一些參數,然后調用RuntimeInit.java中的ZygoteInit函數。

public static final void zygoteInit(String[] argv) throws ZygoteInit.MethodAndArgsCaller {
    commonInit();
    // native層的初始化
    zygoteInitNative();
    ......
    invokeStaticMain(startClass, startArgs); // 調用com.android.server.SystemServer類的main函數
}

SystemServer調用了zygoteInitNative后,將與Binder通信系統建立聯系,這樣SystemServer就可以使用Binder了。zygoteInitNative中調用了onZygoteInit()

virtual void onZygoteInit(){
    sp<ProcessState> proc = ProcessState::self();
    if(proc->supportsProcesses()){
        proc->startThreadPool(); // 啟動一個線程,用於Binder通信
    }
}

而另外一個關鍵函數invokeStaticMain

private static void invokeStaticMain(String className, String[] argv) throws ZygoteInit.MethodAndArgsCaller {
    // 參數傳入,className = "com.android.server.SystemServer"
    ......
    Method m;
    try{
        m = cl.getMethod("main", new Class[] { String[].class }); // 找到com.android.server.SystemServer類的main函數
    } catch(NoSuchMethodException ex){
        ......
    } catch(SecurityException ex){
        ......
    }

    ......
    throw new ZygoteInit.MethodAndArgsCaller(m, argv); // 主動拋出一個異常
}

拋出的異常在哪里被捕獲呢?上面已經給出過了。也就是在ZygoteInit.java中

......
catch(MethodAndArgsCaller caller){
    caller.run(); // 調用caller的run函數
}

而MethodAndArgsCaller的run函數如下:

public void run(){
    try{
        mMethod.invoke(null, new Object[] { mArgs }); // mMethod為com.android.server.SystemServer的main函數
    } catch(IllegalAccessException ex){
        ......
    }
}

為什么要主動拋出這樣一個異常來執行main函數呢?

《深入理解Android 卷I》中是這樣解釋的:這個調用是在ZygoteInit.main中,相當於Native的main函數,也即入口函數,位於堆棧的頂層。如果不采用拋異常的方式,而是在invokeStaticMain那調用,則會浪費之前函數調用所占用的一些調用堆棧。

那么,這個main函數又做了什么工作呢?

public static void main(String[] args){
    System.loadLibrary("android_servers"); // 加載libandroid_servers.so
    init1(args); // 調用native的init1函數
}

其中init1函數創建了一些系統服務,然后把調用線程加入到Binder通信中。期間還通過JNI調用了com.android.server.SystemServer類的init2函數,通過單獨創建一個線程,用以啟動系統的各項服務,如PowerManagerService, BatteryService, WindowManagerService, ActivityManagerService等。

public static final void init2(){
    Slog.i(TAG, "Entered the Android system server!");
    Thread thr = new ServerThread();
    thr.setName("android.server.ServerThread");
    thr.start();
}

這個函數的主要功能是創建新的線程ServerThread,它的override的run方法在SystemServer.java中,主要做了以下一些工作:

private void run() {
    ......
    // Initialize native services.
    System.loadLibrary("android_servers");
    ......
    // Initialize the system context.
    createSystemContext();
    ......
    startCoreServices();
    startOtherServices();
    ......
}
/**
 * Starts some essential services that are not tangled up in the bootstrap process.
 */
private void startCoreServices() {
    // Tracks the battery level.  Requires LightService.
    mSystemServiceManager.startService(BatteryService.class);

    // Tracks application usage stats.
    mSystemServiceManager.startService(UsageStatsService.class);
    mActivityManagerService.setUsageStatsManager(
            LocalServices.getService(UsageStatsManagerInternal.class));
    // Update after UsageStatsService is available, needed before performBootDexOpt.
    mPackageManagerService.getUsageStatsIfNoPackageUsageInfo();

    // Tracks whether the updatable WebView is in a ready state and watches for update installs.
    mSystemServiceManager.startService(WebViewUpdateService.class);
}
/**
 * Starts a miscellaneous grab bag of stuff that has yet to be refactored
 * and organized.
 */
private void startOtherServices() {
    ......
    try {
        Slog.i(TAG, "Reading configuration...");
        SystemConfig.getInstance();

        traceBeginAndSlog("StartSchedulingPolicyService");
        ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
        Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);

        mSystemServiceManager.startService(TelecomLoaderService.class);

        traceBeginAndSlog("StartTelephonyRegistry");
        telephonyRegistry = new TelephonyRegistry(context);
        ServiceManager.addService("telephony.registry", telephonyRegistry);
        Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
...... } ...... }

至此,讓我們總結一下SystemServer

1)、ZygoteInit調用startSystemServer創建system_server進程。

2)、SystemServer調用handleSystemServerProcess完成自己的使命。

3)、handleSystemServerProcess拋出異常,最終調用com.android.server.SystemServer的main函數。

4)、main函數加載libandroid_server.so並調用native的init1函數。

5)、init1函數通過JNI調用com.android.server.SystemServer類的init2函數。init2函數創建一個線程,用於加載各種service。

6)、init1函數最終加入到Binder通信系統。 

4 Zygote的分裂

zygote分裂出system_server后,就等待runSelectLoopMode等待並處理來自客戶的請求。那么,誰會向zygote發送消息呢?這里以一個activity的啟動為例作分析。

4.1 ActivityManagerService發送請求

ActivityManagerService由SystemServer創建。假設通過startActivity來啟動一個新的acitivity,而這個activity屬於一個還未啟動的進程,那么這個進程又如何啟動呢?先看ActivityManagerService中的startProcessLocked函數

private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr){
    ......
    int pid = Process.start("android.app.ActivityThread", mSimpleProcessManagement ? app.processName : null, uid, uid, gids, debugFlags, null);
    ......
}

Process.start位於android.os.Process中,而它又調用了startViaZygote, zygoteSendArgsAndGetPid。而在zygoteSendArgsAndGetPid中,首先打開了和zygote通信的socket,接着把請求的參數發到zygote,然后讀取zygote處理完的結果,得知是某個進程的pid。

由於ActivityManagerService駐留於SystemServer進程中,所以正是SystemServer向Zygote發送了消息。

還記得前面提過的runSelectLoopMode函數么?它在里面又調用了ZygoteConnection的runOnce

boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
    try{
        args = readArgumentList(); // 讀取SystemServer發送過來的參數
        descriptors = mSocket.getAncillaryFileDescriptors();
    } 

    ......

    pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, rlimits);
    ......
    if(pid ==0){
        handleClientProc(parsedArgs, descriptors, newStderr); //子進程處理
        return true;
    } else {
        return handleParentProc(pid, descriptors, parsedArgs);
    }
}

由此可見,zygote分裂子進程后,自己將在handleParentProc中做一些掃尾工作,然后繼續等待請求進行下一次分裂。

4.2 zygote響應請求的流程

1)、Zygote進程調用runSelectLoopMode

2)、SystemServer進程發送消息到Zygote

3)、Zygote通過fork創建子進程

4)、子進程調用android.app.ActivityThread的main函數

5 Home界面啟動

待Application Framework層的ActivityManagerService准備就緒后,就會通知各個模塊,繼續執行上層應用。

先附一張體系結構圖:

    // We now tell the activity manager it is okay to run third party
    // code.  It will call back into us once it has gotten to the state
    // where third party code can really run (but before it has actually
    // started launching the initial applications), for us to complete our
    // initialization.
    mActivityManagerService.systemReady(new Runnable() {
        @Override
        public void run() {
            Slog.i(TAG, "Making services ready");
            ......
            try {
                mActivityManagerService.startObservingNativeCrashes();
            } catch (Throwable e) {
                reportWtf("observing native crashes", e);
            }
            ......
            try {
                if (networkScoreF != null) networkScoreF.systemReady();
            } catch (Throwable e) {
                reportWtf("making Network Score Service ready", e);
            }
            ......
            try {
                if (networkManagementF != null) networkManagementF.systemReady();
            } catch (Throwable e) {
                reportWtf("making Network Managment Service ready", e);
            }
            ......
            try {
                if (networkStatsF != null) networkStatsF.systemReady();
            } catch (Throwable e) {
                reportWtf("making Network Stats Service ready", e);
            }
            ......
            try {
                if (networkPolicyF != null) networkPolicyF.systemReady();
            } catch (Throwable e) {
                reportWtf("making Network Policy Service ready", e);
            }
            ......
            try {
                if (connectivityF != null) connectivityF.systemReady();
            } catch (Throwable e) {
                reportWtf("making Connectivity Service ready", e);
            }
            ......
            try {
                if (audioServiceF != null) audioServiceF.systemReady();
            } catch (Throwable e) {
                reportWtf("Notifying AudioService running", e);
            }
            ......
        }
    });      
public void systemReady(final Runnable goingCallback) {
    ......
    // Start up initial activity.
    mBooting = true;
    startHomeActivityLocked(mCurrentUserId, "systemReady");
    ......
}

這樣一來,就啟動了Home界面,完成了整個Android啟動流程。

下面是啟動流程圖:

 

參考:

《深入理解Android 卷I》 鄧凡平 著

Android啟動過程深入解析

Android系統啟動過程


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM