Android系統(tǒng)啟動(dòng)流程

本文從Android init的main函數(shù)開始講解。

1、 init進(jìn)程

init進(jìn)程啟動(dòng)是從源碼system/core/init/main.cpp中的main()函數(shù)作為入口的,所以我們先來(lái)看看這個(gè)位置:

int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
    __asan_set_error_report_callback(AsanReportCallback);
#endif

    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }

    if (argc > 1) {
        if (!strcmp(argv[1], "subcontext")) {
            android::base::InitLogging(argv, &android::base::KernelLogger);
            const BuiltinFunctionMap function_map;

            return SubcontextMain(argc, argv, &function_map);
        }

        if (!strcmp(argv[1], "selinux_setup")) {
            return SetupSelinux(argv);
        }

        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }

    return FirstStageMain(argc, argv);
}

第一次啟動(dòng)main函數(shù)的時(shí)候沒(méi)有攜帶參數(shù),執(zhí)行的是FirstStageMain(argc, argv);改函數(shù)在system\core\init\first_stage_init.cpp路徑下,內(nèi)容為:

int FirstStageMain(int argc, char** argv) {
    .....

    const char* path = "/system/bin/init";
    const char* args[] = {path, "selinux_setup", nullptr};
    execv(path, const_cast<char**>(args));

    .....
}

可以看到這里又再次執(zhí)行了system/core/init/main.cpp中的main()函數(shù),且攜帶了selinux_setup參數(shù),回到main()函數(shù),這次執(zhí)行了SetupSelinux(argv),該函數(shù)在system\core\init\selinux.cpp文件,內(nèi)容如下:

// This function initializes SELinux then execs init to run in the init SELinux context.
int SetupSelinux(char** argv) {
    InitKernelLogging(argv);

    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }

    // Set up SELinux, loading the SELinux policy.
    SelinuxSetupKernelLogging();
    SelinuxInitialize();

    // We're in the kernel domain and want to transition to the init domain.  File systems that
    // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
    // but other file systems do.  In particular, this is needed for ramdisks such as the
    // recovery image for A/B devices.
    if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
        PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
    }

    const char* path = "/system/bin/init";
    const char* args[] = {path, "second_stage", nullptr};
    execv(path, const_cast<char**>(args));

    // execv() only returns if an error happened, in which case we
    // panic and never return from this function.
    PLOG(FATAL) << "execv(\"" << path << "\") failed";

    return 1;
}

可以看到這里又又次執(zhí)行了system/core/init/main.cpp中的main()函數(shù),且攜帶了second_stage參數(shù),回到main()函數(shù),這次執(zhí)行了SecondStageMain(argc, argv),該函數(shù)在system\core\init\init.cpp文件,內(nèi)容如下:

int SecondStageMain(int argc, char** argv) {
    ....

    LoadBootScripts(am, sm);

    ....

    while (true) {
        ....

        if (auto result = epoll.Wait(epoll_timeout); !result) {
            LOG(ERROR) << result.error();
        }
    }

    return 0;
}

這里可看到該函數(shù)先是執(zhí)行了LoadBootScripts()這個(gè)函數(shù),之后阻塞在了epoll上。LoadBootScripts()內(nèi)容如下:

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);

    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        parser.ParseConfig("/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
            late_import_paths.emplace_back("/system/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
        if (!parser.ParseConfig("/product_services/etc/init")) {
            late_import_paths.emplace_back("/product_services/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}

可以看到改函數(shù)解析了一個(gè)init.rc文件,改文件在system\core\rootdir\init.rc目錄下,其中有內(nèi)容:

on zygote-start && property:ro.crypto.state=unencrypted
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start netd
    start zygote
    start zygote_secondary

可以看到是想要啟動(dòng)zygote,這個(gè)zygote對(duì)應(yīng)的.rc文件在同目錄下,有不同位機(jī)器的腳本,以64位的init.zygote64.rc為例,有內(nèi)容如下:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

可以看到這里似乎想啟動(dòng)一個(gè)/system/bin/app_process程序,之后我們就來(lái)到了zygote進(jìn)程,而原來(lái)的init進(jìn)程上文已交代過(guò)了,解析完.rc文件之后便阻塞在了epoll上。/system/bin/app_process這個(gè)程序的源文件目錄為frameworks\base\cmds\app_process\app_main.cpp同樣看它的main()函數(shù):

int main(int argc, char* const argv[])
{
    ....
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }
    ....

    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    }
}

可以看到前文中啟動(dòng)app_process程序是帶了--zygote--start-system-server參數(shù)的,所以這里zygote = true將會(huì)執(zhí)行runtime.start("com.android.internal.os.ZygoteInit", args, zygote),runtime.start()函數(shù)實(shí)際上調(diào)用的是\frameworks\base\core\jni\AndroidRuntime.cpp中的void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)函數(shù),其內(nèi)容如下:

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ....

    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);

    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    /*
     * We want to call main() with a String array with arguments in it.
     * At present we have two arguments, the class name and an option string.
     * Create an array to hold them.
     */
    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;

    stringClass = env->FindClass("java/lang/String");
    assert(stringClass != NULL);
    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
    assert(strArray != NULL);
    classNameStr = env->NewStringUTF(className);
    assert(classNameStr != NULL);
    env->SetObjectArrayElement(strArray, 0, classNameStr);

    for (size_t i = 0; i < options.size(); ++i) {
        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
        assert(optionsStr != NULL);
        env->SetObjectArrayElement(strArray, i + 1, optionsStr);
    }

    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }
    free(slashClassName);

    ALOGD("Shutting down VM\n");
    if (mJavaVM->DetachCurrentThread() != JNI_OK)
        ALOGW("Warning: unable to detach main thread\n");
    if (mJavaVM->DestroyJavaVM() != 0)
        ALOGW("Warning: VM did not shut down cleanly\n");
}

這里先使用了startVm(&mJavaVM, &env, zygote)創(chuàng)建了java虛擬機(jī),然后使用startReg(env)注冊(cè)了jni的一些函數(shù),因?yàn)橹蟮絡(luò)ava環(huán)境后需要調(diào)用native層的一些函數(shù)。最后調(diào)用env->CallStaticVoidMethod(startClass, startMeth, strArray)調(diào)用了java中的一個(gè)方法,這個(gè)方法是ZygoteInit.javamain方法。其內(nèi)容如下:

    @UnsupportedAppUsage
    public static void main(String argv[]) {
        ....
        try {
            ....

            // In some configurations, we avoid preloading resources and classes eagerly.
            // In such cases, we will preload things prior to our first fork.
            if (!enableLazyPreload) {
                bootTimingsTraceLog.traceBegin("ZygotePreload");
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                        SystemClock.uptimeMillis());
                preload(bootTimingsTraceLog);
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                        SystemClock.uptimeMillis());
                bootTimingsTraceLog.traceEnd(); // ZygotePreload
            } else {
                Zygote.resetNicePriority();
            }

            ....

            zygoteServer = new ZygoteServer(isPrimaryZygote);

            if (startSystemServer) {
                Runnable r = forkSystemServer(abiList, zygoteSocketName, 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;
                }
            }

            Log.i(TAG, "Accepting command socket connections");

            // The select loop returns early in the child process after a fork and
            // loops forever in the zygote.
            caller = zygoteServer.runSelectLoop(abiList);
        } catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex);
            throw ex;
        } finally {
            if (zygoteServer != null) {
                zygoteServer.closeServerSocket();
            }
        }

        // We're in the child process and have exited the select loop. Proceed to execute the
        // command.
        if (caller != null) {
            caller.run();
        }
    }

這段代碼有幾處重要函數(shù),功能如下:

preload(bootTimingsTraceLog); // 預(yù)加載類及一些資源文件,加快進(jìn)程的啟動(dòng)
zygoteServer = new ZygoteServer(isPrimaryZygote); // 創(chuàng)建本地socket通信
Runnable r = forkSystemServer // 啟動(dòng) SystemServer 進(jìn)程
caller = zygoteServer.runSelectLoop(abiList); // 死循環(huán),接收并處理AMS傳過(guò)來(lái)的消息,比如fork app進(jìn)程。

所以zygote分為native和java層,分別有如下功能:

native:
1.初始化運(yùn)行環(huán)境,創(chuàng)建jvm Android的虛擬機(jī) 
2.注冊(cè)jni
3.調(diào)用 zygoteinit.main

java
1.預(yù)加載 -- 加快進(jìn)程啟動(dòng)
2.創(chuàng)建sokcet通信
3.啟動(dòng)SystemServer進(jìn)程
3.循環(huán)接收處理socket事件。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容