Android 9.0 系統啟動流程


極力推薦文章:歡迎收藏
Android 干貨分享

閱讀五分鍾,每日十點,和您一起終身學習,這里是程序員Android

本篇文章主要介紹 Android 開發中的部分知識點,通過閱讀本篇文章,您將收獲以下內容:

一、啟動流程概述
二、Android啟動分析
三、init 進程啟動分析
四、init 啟動腳本分析
五、init 進程分析
六、init 腳本執行
七、init 進程守護
八、init rc 腳本啟動Zygote
九、啟動分析小結

一、 啟動流程概述

Android 啟動流程跟 Linux 啟動類似,大致分為如下五個階段。

  • 1.開機上電,加載固化的ROM
  • 2.加載BootLoader,拉起Android OS
  • 3.加載Uboot,初始外設,引導Kernel啟動等。
  • 4.啟動Kernel,加載驅動,硬件。
  • 5.啟動Android,掛載分區,加載驅動、服務,init 進程等。

Android系統啟動大致過程如下:
Android 啟動過程

由於水平有限,無法深入了理解驅動層代碼,本文主要對 Android 上層啟動流程進行分析。

二、Android啟動分析

Uboot啟動Kernel完成系統設置后,會首先在系統中尋找init.rc文件(文件多存放在/system/core/init目錄下),並啟動init進程。

Android啟動 主要包含如下兩種

  • 1.啟動腳本
  • 2.Zygote 啟動

Android 啟動分析主要內容如下:

Android 啟動分析

三、init 進程啟動分析

Init進程是是Android 啟動的第一個進程,進程號為1,是Android 的系統啟動的核心進程,主要用來創建Zygote、屬性服務等。源碼主要存在\system\core\init目錄下。

常見init.xxx.rc 進程如下:
常見init.xxx.rc 進程

init 進程的入口 main 函數
init.cpp 中的main 函數,是init進程的入口函數(代碼路徑:\system\core\init\init.cpp)

main 函數主要做的事情

1.創建掛載啟動所需的文件系統(tmpfs、 devpts、 proc、 sysfs、 selinuxfs等)
2.初始化並啟動屬性服務
3.解析init.rc 腳本配置文件,並啟動Zygote 進程。
init 進程

init.cpp main 函數實現代碼如下:

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

    if (!strcmp(basename(argv[0]), "watchdogd")) {
		//啟動看門狗函數
        return watchdogd_main(argc, argv);
    }

    if (argc > 1 && !strcmp(argv[1], "subcontext")) {
        InitKernelLogging(argv);
        const BuiltinFunctionMap function_map;
        return SubcontextMain(argc, argv, &function_map);
    }

    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }

    bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);

    //啟動第一階段
    if (is_first_stage) {
        boot_clock::time_point start_time = boot_clock::now();

        // 清理 umask.
        umask(0);

        clearenv();
        setenv("PATH", _PATH_DEFPATH, 1);

        // 在RAM內存上獲取基本的文件系統,剩余的被 rc 文件所用
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        #define MAKE_STR(x) __STRING(x)
        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
        // 非特權應用不能使用 Android 命令行
        chmod("/proc/cmdline", 0440);
        gid_t groups[] = { AID_READPROC };
        setgroups(arraysize(groups), groups);
        mount("sysfs", "/sys", "sysfs", 0, NULL);
        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);

        mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));

        if constexpr (WORLD_WRITABLE_KMSG) {
            mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
        }

        mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
        mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));

        // Mount staging areas for devices managed by vold
        // See storage config details at http://source.android.com/devices/storage/
        mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
              "mode=0755,uid=0,gid=1000");
        //創建可供讀寫的 vendor目錄
        mkdir("/mnt/vendor", 0755);

        // 在/dev目錄下掛載好 tmpfs 以及 kmsg 
        // 這樣就可以初始化 /kernel Log 系統,供用戶打印log
        InitKernelLogging(argv);

        LOG(INFO) << "init first stage started!";

        if (!DoFirstStageMount()) {
            LOG(FATAL) << "Failed to mount required partitions early ...";
        }

        SetInitAvbVersionInRecovery();

        // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
        global_seccomp();

        // 優先加載selinux log系統, 緊接着初始化selinux
        SelinuxSetupKernelLogging();
        SelinuxInitialize();

        // 添加 selinux 是否啟動成功的log
        if (selinux_android_restorecon("/init", 0) == -1) {
            PLOG(FATAL) << "restorecon failed of /init failed";
        }

        setenv("INIT_SECOND_STAGE", "true", 1);

        static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
        uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
        setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);

        char* path = argv[0];
        char* args[] = { path, nullptr };
        execv(path, args);

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

    //啟動第二階段
    InitKernelLogging(argv);
    LOG(INFO) << "init second stage started!";

    // Set up a session keyring that all processes will have access to. It
    // will hold things like FBE encryption keys. No process should override
    // its session keyring.
    keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);

    // Indicate that booting is in progress to background fw loaders, etc.
    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
    //初始化屬性
    property_init();

    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.
    process_kernel_dt();
    process_kernel_cmdline();

    // Propagate the kernel variables to internal variables
    // used by init as well as the current required properties.
    export_kernel_boot_props();

    // Make the time that init started available for bootstat to log.
    property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
    property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));

    // Set libavb version for Framework-only OTA match in Treble build.
    const char* avb_version = getenv("INIT_AVB_VERSION");
    if (avb_version) property_set("ro.boot.avb_version", avb_version);

    // 清空設置的環境變量
    unsetenv("INIT_SECOND_STAGE");
    unsetenv("INIT_STARTED_AT");
    unsetenv("INIT_SELINUX_TOOK");
    unsetenv("INIT_AVB_VERSION");

    // 設置第二階段的selinux
    SelinuxSetupKernelLogging();
    SelabelInitialize();
    SelinuxRestoreContext();
    //創建 epoll 句柄
    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (epoll_fd == -1) {
        PLOG(FATAL) << "epoll_create1 failed";
    }
    //設置 子進程處理函數
    sigchld_handler_init();

    if (!IsRebootCapable()) {
        // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
        // In that case, receiving SIGTERM will cause the system to shut down.
        InstallSigtermHandler();
    }

    LoadRscRoProps();
    property_load_boot_defaults();
    export_oem_lock_status();
	//啟動屬性服務
    start_property_service();
	//為USB存儲設置udc Contorller, sys/class/udc
    set_usb_controller();

    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);

    subcontexts = InitializeSubcontexts();

    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();

    LoadBootScripts(am, sm);

    // Turning this on and letting the INFO logging be discarded adds 0.2s to
    // Nexus 9 boot time, so it's disabled by default.
    if (false) DumpState();

    am.QueueEventTrigger("early-init");

    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    // ... so that we can start queuing up actions that require stuff from /dev.
    am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
    am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
    am.QueueBuiltinAction(keychord_init_action, "keychord_init");
    am.QueueBuiltinAction(console_init_action, "console_init");

    // Trigger all the boot actions to get us started.
    am.QueueEventTrigger("init");

    // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
    // wasn't ready immediately after wait_for_coldboot_done
    am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");

    // Don't mount filesystems or start core system services in charger mode.
    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");
    }

    // Run all property triggers based on current state of the properties.
    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");

    while (true) {
        // By default, sleep until something happens.
        int epoll_timeout_ms = -1;

        if (do_shutdown && !shutting_down) {
            do_shutdown = false;
            if (HandlePowerctlMessage(shutdown_command)) {
                shutting_down = true;
            }
        }

        if (!(waiting_for_prop || Service::is_exec_service_running())) {
            am.ExecuteOneCommand();
        }
        if (!(waiting_for_prop || Service::is_exec_service_running())) {
            if (!shutting_down) {
                auto next_process_restart_time = RestartProcesses();

                // If there's a process that needs restarting, wake up in time for that.
                if (next_process_restart_time) {
                    epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
                                           *next_process_restart_time - boot_clock::now())
                                           .count();
                    if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
                }
            }

            // If there's more work to do, wake up again immediately.
            if (am.HasMoreCommands()) epoll_timeout_ms = 0;
        }

        epoll_event ev;
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
        if (nr == -1) {
            PLOG(ERROR) << "epoll_wait failed";
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
    }

    return 0;
}

Init 進程源碼分析
Init 進程源碼分析
基於MTK 平台 init.cpp 源碼分析
基於MTK 平台 init.cpp 主要作用

四、 init 啟動腳本分析

init.rc 路徑 一般在system/core/rootdir下,init 腳本是有Android 初始化語言編寫。

Android Init Language 語句類型:

  • 1.Action
  • 2.Command
  • 3.Service
  • 4.Option
  • 5.Import

init.rc分析

Android 啟動腳本

init.rc on

init.rc services

init.rc import

五、init 進程分析

init 進程分析

init 解析腳本分析

init 事件列表

init 事件結構

六 、init 腳本執行

init 進程解析和執行

啟動腳本解析結果

整理事件列表

init 構建事件

Service 事件分類

init 進程執行命令和啟動服務

七、init 進程守護

init進程處理消息事件

    1. 根據Shell 或者系統中消息設置系統prop
    1. 守護系統服務,如果服務退出,重啟退出的服務。

init守護進程

init 處理 prop 消息分析

init 守護服務分析

八、init rc 腳本啟動Zygote

Zygoteclassnamemain .
init.rc 文件配置代碼如下:

... ... 
on nonencrypted
    class_start main
    class_start late_start

on property:sys.init_log_level=*
    loglevel ${sys.init_log_level}

... ...

九、啟動分析小結

啟動分析小結

xmind 小結文件下載地址如下:
小結源文件下載地址

至此,本篇已結束,如有不對的地方,歡迎您的建議與指正。同時期待您的關注,感謝您的閱讀,謝謝!

微信關注公眾號:  程序員Android,領福利


免責聲明!

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



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