国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

基于Android13的系統(tǒng)啟動流程分析(四)之SecondStageMain階段

這篇具有很好參考價值的文章主要介紹了基于Android13的系統(tǒng)啟動流程分析(四)之SecondStageMain階段。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

Android13系統(tǒng)啟動階段大致分為FirstStageMain階段和SecondStageMain,此章主要講SecondStageMain階段
(若分析有誤敬請指教)

一. Android系統(tǒng)啟動基本介紹

在基于Android13的系統(tǒng)啟動流程分析(三)之FirstStageMain階段已經(jīng)講解過android系統(tǒng)啟動的基本介紹了,這里不再單獨(dú)介紹了

二. SecondStageMain源碼分析

我們先看是怎么進(jìn)入該階段的,仍然是由用戶空間層main.cpp調(diào)用,先簡單的說一下第二階段主要是干什么的:

  1. 設(shè)置init進(jìn)程優(yōu)先級并創(chuàng)建/dev/.booting設(shè)備塊代表init正在初始化執(zhí)行中
  2. 初始化屬性服務(wù),也就是會讀取property_contexts文件內(nèi)容以及讀取build.prop內(nèi)容通過MMAP映射到全局內(nèi)存中,也就是對所有進(jìn)程共享該資源
  3. 啟動屬性服務(wù)并創(chuàng)建socket_service,等待新鏈接去更新或新增屬性值
  4. 掛載/apex,vendor_overlay等其他分區(qū)
  5. 檢查設(shè)備是否被unlock解鎖
  6. 持續(xù)監(jiān)控/proc/mounts設(shè)備文件,解析文件中每一行數(shù)據(jù),獲取掛載點(diǎn),掛載分區(qū),文件類型,權(quán)限等。將解析內(nèi)容生成實(shí)體類追加到要掛載的mounts_中并進(jìn)行掛載
  7. 將根目錄下所有的目錄設(shè)置為全局共享,例如對/data設(shè)置為根目錄下的全局共享
  8. 解析init.rc以及其他import了的rc文件,主要解析rc中的:service,on(action),Import,而zygote進(jìn)程正是從解析rc文件中創(chuàng)建的,然后根據(jù)zygote(本質(zhì)上就是一個socket),通過JNI調(diào)用到上層代碼,再fork出systemServer.java
  9. 讓init進(jìn)程無限循環(huán),因?yàn)橹鬟M(jìn)程不能退出,退出即代表發(fā)生異常
  10. 處理sm(ServiceList)中服務(wù)超時重啟相關(guān)(init.rc中的service),若rc中啟動的服務(wù)啟動超時則會讓其服務(wù)重新啟動
...
    if (argc > 1) {
       ...
        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);//第二階段執(zhí)行
        }
    }
    return FirstStageMain(argc, argv); //第一階段執(zhí)行
}

1. int SecondStageMain(int argc, char** argv)分析

位于/system/core/init/init.cpp,直接上代碼,代碼注釋中分步驟來分析

int SecondStageMain(int argc, char** argv) {
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        // 針對產(chǎn)生異常的進(jìn)程進(jìn)行信號處理,確保子進(jìn)程能重啟,如果主進(jìn)程pid=1發(fā)生異常則觸發(fā)crash
        // 已經(jīng)在上個文章分析過該函數(shù)
        InstallRebootSignalHandlers();
    }
 	...
    // 初始化kernel log,所有的kernel log均輸出在/dev/kmsg設(shè)備節(jié)點(diǎn)上
    SetStdioToDevNull(argv);
    InitKernelLogging(argv);
    LOG(INFO) << "init second stage started!";
    ...
    // Init不應(yīng)該因?yàn)橐蕾囉谌魏纹渌M(jìn)程而崩潰,因此我們忽略主進(jìn)程的信號管道信息
    // 但我們不想忽略子進(jìn)程的SIGPIPE(信號管道),因此我們?yōu)樾盘柼幚沓绦蛟O(shè)置了一個no op函數(shù)
    // SIGPIPE信號產(chǎn)生的場景舉例
    // ① 初始時,C、S連接建立,若某一時刻,C端進(jìn)程宕機(jī)或者被KILL而終止(終止的C端進(jìn)程將會關(guān)閉打開的文件描述符,即向S端發(fā)送FIN段),S端收到FIN后,響應(yīng)ACK
    // ② 假設(shè)此時,S端仍然向C端發(fā)送數(shù)據(jù):當(dāng)?shù)谝淮螌憯?shù)據(jù)后,S端將會收到RST分節(jié); 當(dāng)收到RST分節(jié)后,第二次寫數(shù)據(jù)后,S端將收到SIGPIPE信號(S端進(jìn)程被終止)
    {
        struct sigaction action = {.sa_flags = SA_RESTART};
        action.sa_handler = [](int) {};
        // sigaction是一個函數(shù),可以用來查詢或設(shè)置信號處理方式
        sigaction(SIGPIPE, &action, nullptr);
    }

    //  MIN_OOM_SCORE_ADJUST = -1000;
    //  MAX_OOM_SCORE_ADJUST = 1000;
    //  設(shè)置進(jìn)程的優(yōu)先級,例如APK優(yōu)先級是AMS計(jì)算出來并下發(fā)到/proc/1/oom_score_adj
    //  統(tǒng)一由init進(jìn)程設(shè)置/proc/**/oom_score_adj為-1000優(yōu)先級
    if (auto result =
                WriteFile("/proc/1/oom_score_adj", StringPrintf("%d", DEFAULT_OOM_SCORE_ADJUST));
        !result.ok()) {
        LOG(ERROR) << "Unable to write " << DEFAULT_OOM_SCORE_ADJUST
                   << " to /proc/1/oom_score_adj: " << result.error();
    }

  	...
    // 創(chuàng)建 /dev/.booting 文件,就是個標(biāo)記,表示booting進(jìn)行中
    // is_booting()函數(shù)會依靠空文件".booting"來判斷是否進(jìn)程處于初始化中,初始化結(jié)束后,這個文件會被刪除
    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
    
    // 當(dāng)設(shè)備解鎖時,允許adb root
    // 如果設(shè)備unlocked(解鎖了),則會修改selinux規(guī)則,放大用戶權(quán)限,這一點(diǎn)在第一階段已經(jīng)完成了
    // 并設(shè)置了INIT_FORCE_DEBUGGABLE環(huán)境變量,這里只是根據(jù)環(huán)境變量獲取第一階段的內(nèi)容
    const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
    bool load_debug_prop = false;
    if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
        load_debug_prop = "true"s == force_debuggable_env;
    }
    unsetenv("INIT_FORCE_DEBUGGABLE");
    
    // 如果設(shè)備未unlock,則卸載關(guān)于debug版本的/debug_ramdisk
    // 讓屬性值讀取/ramdisk而不是/debug_ramdisk,因?yàn)榉莡nlock,不需要debug ramdisk
    if (!load_debug_prop) {
    	// setup 1
        UmountDebugRamdisk();
    }

    // 初始化屬性服務(wù)
    // 獲取system/build.prop,vendor/build.prop,/odm/build.prop,/product/build.prop,等其他build.prop并加載到properties map結(jié)構(gòu)中
    // 然后會將該properties結(jié)構(gòu),通過MMAP映射到全局內(nèi)存中,供所有進(jìn)程調(diào)用,主要ro是只讀,只能夠?qū)懸淮危闯跏蓟瘯r給的一次值
    // 注意build.prop有優(yōu)先級,product下的優(yōu)先級最高,因?yàn)槭莔ap結(jié)構(gòu),針對key相同的屬性值時,會覆蓋
    // setup 2
    PropertyInit();

    // Umount second stage resources after property service has read the .prop files.
    // 在屬性服務(wù)讀取.prop文件后,將卸載/second_stage_resources,因?yàn)橐呀?jīng)用不到了,已經(jīng)將屬性值加載到內(nèi)存當(dāng)中了
    UmountSecondStageRes();

    // Umount the debug ramdisk after property service has read the .prop files when it means to.
    // 若是debug版本,已經(jīng)獲取了屬性值過后,也將卸載/debug_ramdisk
    if (load_debug_prop) {
        UmountDebugRamdisk();
    }

    // 掛載第二階段(該階段)的文件系統(tǒng),第一階段已經(jīng)掛載了很多基本的文件系統(tǒng)了以及重要的分區(qū)
    // 掛載/apex:簡單點(diǎn)說apex為了解決性能而產(chǎn)生的機(jī)制,APK可以通過內(nèi)置升級,但系統(tǒng)升級可是個大問題
    // setup 3
    MountExtraFilesystems();
	...
    // 之前初始化了屬性服務(wù),這里將開始屬性服務(wù),其實(shí)它就是一個socket
    // 創(chuàng)建socket,處理客戶端發(fā)來的請求,決定是更新屬性值還是新增屬性值
    // setup 4
    StartPropertyService(&property_fd);
    ...
    // 根據(jù)ro.vndk.version 版本號,將/system/vendor_overlay和/product/vendor_overlay掛載在vendor上
    // 也就是會覆蓋vendor分區(qū)內(nèi)容
    // setup 5
    fs_mgr_vendor_overlay_mount_all();
    // 根據(jù)ro.oem_unlock_supported屬性值來決定是否可以對設(shè)備進(jìn)行unlock(解鎖)
    // 若ro.oem_unlock_supported:「1」則代表 設(shè)備支持刷寫unlock,若不支持該值為0
    // 如果設(shè)備支持刷寫解鎖,ro.boot.verifiedbootstate則會為orange,根據(jù)orange狀態(tài),把a(bǔ)ndroidboot.flash.locked設(shè)置為1
    // 如果設(shè)備不支持刷新解鎖,ro.boot.verifiedbootstate則會為green,根據(jù)orange狀態(tài),把a(bǔ)ndroidboot.flash.locked設(shè)置為0
    // androidboot.flash.locked在系統(tǒng)啟動完成后會形成屬性值
    // (或 /firmware/android/flash.locked DT 屬性)設(shè)置為“1”(如果已鎖定)或“0”(如果已解鎖)來指示鎖定狀態(tài)。
    export_oem_lock_status();
    // 持續(xù)監(jiān)控/proc/mounts 節(jié)點(diǎn)(fopen("/proc/mounts", "re")),主要是解析該文件
    // 解析文件中每一行數(shù)據(jù),獲取掛載點(diǎn),掛載分區(qū),文件類型,權(quán)限等(空格分割/dev/block/dm-33 /mnt/pass_through/0/emulated ext4 rw)
    // 將解析內(nèi)容生成實(shí)體類追加到要掛載的mounts_中,主要就是對mounts文件解析,更新mounts_中的信息
    // setup 6
    MountHandler mount_handler(&epoll);
    ...
    // 將根目錄下所有的目錄設(shè)置為全局共享
    // 將根目錄/{分區(qū)}類型設(shè)置為共享,以便默認(rèn)情況下所有進(jìn)程都可以看到任何裝載事件(例如/data)
    if (!SetupMountNamespaces()) {
        PLOG(FATAL) << "SetupMountNamespaces failed";
    }

	...
	// setup 7
    // 創(chuàng)建ActionManager對象和ServiceList對象
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();
    // 加載rc文件,保存到action manager和service list中
    // rc文件中:action 使用 ActionParser,而 service 使用 ServiceParser 解析
    // 主要解析rc中的:service,on,Import,包含了zygote.rc,路徑:/system/bin/app_process64
    // 在文件系統(tǒng)掛載的第一階段,system/vendor分區(qū)已經(jīng)成功掛載,而其它分區(qū)的掛載則通過rc來掛載
    // 后面主要分析一下on early-init和on init和zygote
    LoadBootScripts(am, sm);
	...
	// setup 8
    // 構(gòu)建action和觸發(fā)器(on early-init),放到event_queue,等待執(zhí)行函數(shù)
    am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
    am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
    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(SetMmapRndBitsAction, "SetMmapRndBits");
    Keychords keychords;
    am.QueueBuiltinAction(
            [&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
                for (const auto& svc : ServiceList::GetInstance()) {
                    keychords.Register(svc->keycodes());
                }
                keychords.Start(&epoll, HandleKeychord);
                return {};
            },
            "KeychordInit");

    // Trigger all the boot actions to get us started.
    // 構(gòu)建action和觸發(fā)器(on init),放到event_queue,等待執(zhí)行函數(shù)
    am.QueueEventTrigger("init");

    // Don't mount filesystems or start core system services in charger mode.
    // 如果是充電模式則不需要掛載文件系統(tǒng)和不要啟動核心服務(wù)
    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.
    // 運(yùn)行所有屬性觸發(fā)器(action),例如 on property
    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");

    // Restore prio before main loop
    // 設(shè)置進(jìn)程優(yōu)先級,主進(jìn)程不能被銷毀和退出,循環(huán)處理rc中的服務(wù)相關(guān)
    setpriority(PRIO_PROCESS, 0, 0);
    while (true) {
        // By default, sleep until something happens.
        auto epoll_timeout = std::optional<std::chrono::milliseconds>{};

        auto shutdown_command = shutdown_state.CheckShutdown();
        if (shutdown_command) {
            LOG(INFO) << "Got shutdown_command '" << *shutdown_command
                      << "' Calling HandlePowerctlMessage()";
            HandlePowerctlMessage(*shutdown_command);
            shutdown_state.set_do_shutdown(false);
        }

        if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
            // 執(zhí)行隊(duì)列中的action
            // 隊(duì)列中依次執(zhí)行每個action中攜帶command對應(yīng)的執(zhí)行函數(shù)
            am.ExecuteOneCommand();
        }
        if (!IsShuttingDown()) {
            // 處理sm(ServiceList)中服務(wù)超時重啟相關(guān)(init.rc中的service)
            auto next_process_action_time = HandleProcessActions();
            if (next_process_action_time) {
                epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
                        *next_process_action_time - boot_clock::now());
                if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
            }
        }
...

    return 0;
}

2. SecondStageMain(int argc, char** argv)----->setup 1步驟

先貼一下setup 1的代碼塊,主要分析UmountDebugRamdisk函數(shù)

    if (!load_debug_prop) {
    	// setup 1
        UmountDebugRamdisk();
    }
    
static void UmountDebugRamdisk() {
    if (umount("/debug_ramdisk") != 0) {
        PLOG(ERROR) << "Failed to umount /debug_ramdisk";
    }
}    

如果設(shè)備未unlock,則卸載關(guān)于debug版本的/debug_ramdisk,讓屬性值讀取/ramdisk而不是/debug_ramdisk

3. SecondStageMain(int argc, char** argv)----->setup 2步驟

該步驟主要作用是初始化屬性值服務(wù),這里只是一個初始化的動作

    // setup 2
    PropertyInit();
  • 初始化屬性服務(wù),獲取system/build.prop,vendor/build.prop,/odm/build.prop,/product/build.prop,等其他build.prop并加載到properties map結(jié)構(gòu)中,然后會將該properties結(jié)構(gòu),通過MMAP映射到全局內(nèi)存中,供所有進(jìn)程調(diào)用,主要ro是只讀,只能夠?qū)懸淮危闯跏蓟瘯r給的一次值,注意build.prop有優(yōu)先級,product下的優(yōu)先級最高,因?yàn)槭莔ap結(jié)構(gòu),針對key相同的屬性值時,會覆蓋
  • PropertyInit位于/system/core/init/property_service.cpp
void PropertyInit() {
   ...
    // 建立屬性服務(wù)設(shè)備文件(linux思想,萬物皆文件系統(tǒng))
    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
    // 創(chuàng)建序列化過后的propertyInfo實(shí)體,主要就是讀取property_contexts文件
    CreateSerializedPropertyInfo();
    // 這里主要步驟是:通過mmap映射,將文件(/dev/__properties__/{..})映射進(jìn)內(nèi)存(初始化屬性內(nèi)存映射文件)
    // 這里將文件映射進(jìn)內(nèi)存,用于后續(xù)對__prooerties__目錄(也可以說是文件,將代碼塊映射為file類型)進(jìn)行內(nèi)存共享
    if (__system_property_area_init()) {
        LOG(FATAL) << "Failed to initialize property area";
    }
    // 加載/dev/__properties__/property_info,此文件是序列化過的,無法直接查看內(nèi)容
    if (!property_info_area.LoadDefaultPath()) {
        LOG(FATAL) << "Failed to load serialized property info file";
    }

    // 讀取/proc/device-tree/firmware/android/目錄下的文件,生成ro.boot.xxx屬性值
    // 這三個函數(shù)主要就是生成ro.boot.xx屬性值,這里不詳細(xì)研究
    ProcessKernelDt();
    ProcessKernelCmdline();
    ProcessBootconfig();

    // 初始化ro.xx,將ro.boot.xx的屬性值復(fù)制給ro.xxx
    // { "ro.boot.serialno",   "ro.serialno",   UNSET, },
    // { "ro.boot.mode",       "ro.bootmode",   "unknown", },
    // { "ro.boot.baseband",   "ro.baseband",   "unknown", },
    // { "ro.boot.bootloader", "ro.bootloader", "unknown", },
    // { "ro.boot.hardware",   "ro.hardware",   "unknown", },
    // { "ro.boot.revision",   "ro.revision",   "0", },
    ExportKernelBootProps();

    // 讀取{system/vendor/odm/product}/build.prop等...
    // 將build.prop通過MMAP映射到全局內(nèi)存中,供所有進(jìn)程訪問 
    PropertyLoadBootDefaults();

調(diào)用__system_property_area_init通過mmap映射,將文件(/dev/properties/{包含了上下文和propertys_info實(shí)體:保存了property_contexts文件內(nèi)容})映射進(jìn)內(nèi)存(初始化屬性內(nèi)存映射文件), 這里將文件映射進(jìn)內(nèi)存,用于后續(xù)對__prooerties__目錄(也可以說是文件,將代碼塊映射為file類型)進(jìn)行內(nèi)存共享

  • CreateSerializedPropertyInfo:

    創(chuàng)建序列化過后的屬性值信息(既然序列化了,那肯定是要跨進(jìn)程通信)
    (1).讀取{system_ext,vendor,product,odm,system}_property_contexts屬性值安全上下文并賦值給:property_infos
    (2).property_infos屬于容器類型,讀取不同的property_contexts將會追加到末尾,而不是覆蓋原本內(nèi)容
    (3).將property_infos實(shí)體序列化,使其可以跨進(jìn)程傳遞消息
    (4).將property_infos實(shí)體寫入/dev/properties/property_info驅(qū)動節(jié)點(diǎn)中

  • PropertyLoadBootDefaults
    這里會將屬性值全部寫入build.prop里,分為:system/build.prop,vendor/build.prop,/odm/build.prop,/product/build.prop,注意是有優(yōu)先級順序的,按先后順序覆蓋,獲取build.prop分別是直接從指定文件里獲取和從指定分區(qū)中獲取,這兩個方式作用都一樣,只不過第二種需要區(qū)分出分區(qū)里是否存在{partition}/{etc}/build.prop,有的分區(qū)是不存在{partition}/etc/build.prop這個文件,而是直接存在于{partition}/build.prop

一切都寫到了注釋里,繼續(xù)分析比較重要的2個函數(shù)CreateSerializedPropertyInfoPropertyLoadBootDefaults

3.1 CreateSerializedPropertyInfo
void CreateSerializedPropertyInfo() {
    auto property_infos = std::vector<PropertyInfoEntry>();
    //判斷文件是否存在,并判斷文件是否可寫(屬性服務(wù)的安全上下文,之前有提過設(shè)備節(jié)點(diǎn),服務(wù),屬性值都要遵守selinux規(guī)則)
    if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {
        // 加載property_contexts文件,該文件內(nèi)容都是配置的屬性值上下文,屬于selinux相關(guān)知識
        // 通過ParsePropertyInfoFile解析該文件,得到property_infos
        if (!LoadPropertyInfoFromFile("/system/etc/selinux/plat_property_contexts",
                                      &property_infos)) {
            return;
        }
        // 如果這里system_ext/vendor/product沒有掛載上(例如在恢復(fù)的情況下,vendor分區(qū)將不會安裝),則無法繼續(xù)加載該上下文,該分區(qū)會在第一階段掛載
        // 從下面的代碼可以看出來,property_infos的是容器類型vector<PropertyInfoEntry>()
        // 所以這里并沒有優(yōu)先級也沒有以哪個property_contexts為準(zhǔn),而是根據(jù)是否存在對應(yīng)的分區(qū)而append加載
        // 也就是在對應(yīng)后面追加內(nèi)容,而不是覆蓋:property_infos->emplace_back(property_info_entry);
        if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
            LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",
                                     &property_infos);
        }
        if (!LoadPropertyInfoFromFile("/vendor/etc/selinux/vendor_property_contexts",
                                      &property_infos)) {
            // Fallback to nonplat_* if vendor_* doesn't exist.
            LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts",
                                     &property_infos);
        }
        if (access("/product/etc/selinux/product_property_contexts", R_OK) != -1) {
            LoadPropertyInfoFromFile("/product/etc/selinux/product_property_contexts",
                                     &property_infos);
        }
        if (access("/odm/etc/selinux/odm_property_contexts", R_OK) != -1) {
            LoadPropertyInfoFromFile("/odm/etc/selinux/odm_property_contexts", &property_infos);
        }
     // 若/system/etc/selinux/plat_property_contexts無法讀取,則else
    } else {
        // 由于system下的安全上下文未創(chuàng)建,則可能是system出現(xiàn)異常未掛載上,或者供應(yīng)商修改過plat_property_contexts
        // 一般供應(yīng)商都是復(fù)寫,而不會直接更改文件名稱
        // 若找不到該文件,則加載根目錄下的這些屬性值安全上下文
        if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {
            return;
        }
        LoadPropertyInfoFromFile("/system_ext_property_contexts", &property_infos);
        if (!LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos)) {
            // Fallback to nonplat_* if vendor_* doesn't exist.
            LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
        }
        LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
        LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
    }

    // 序列化property_infos實(shí)體,使其可以跨進(jìn)程傳遞
    auto serialized_contexts = std::string();
    auto error = std::string();
    if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "string", &serialized_contexts,
                   &error)) {
        LOG(ERROR) << "Unable to serialize property contexts: " << error;
        return;
    }


    // 將property_infos寫入/dev/__properties__/property_info設(shè)備文件中
    constexpr static const char kPropertyInfosPath[] = "/dev/__properties__/property_info";
    if (!WriteStringToFile(serialized_contexts, kPropertyInfosPath, 0444, 0, 0, false)) {
        PLOG(ERROR) << "Unable to write serialized property infos to file";
    }
    selinux_android_restorecon(kPropertyInfosPath, 0);
}

以上的步驟都是對property_infos變量進(jìn)行賦值,數(shù)據(jù)就是property_contexts文件內(nèi)容,而property_infos是屬于vector<PropertyInfoEntry>()容器類型,而每次加載property_infos時都是調(diào)用的property_infos->emplace_back(property_info_entry);在容器后面追加數(shù)據(jù),不會覆蓋原有的數(shù)據(jù)

讀取property_contexts文件內(nèi)容,將內(nèi)容傳遞給property_infos實(shí)體

3.2 PropertyLoadBootDefaults
void PropertyLoadBootDefaults() {
    std::map<std::string, std::string> properties;
    // 如果是恢復(fù)模式則加載/prop.default
    if (IsRecoveryMode()) {
        load_properties_from_file("/prop.default", nullptr, &properties);
    }

    // 這里還沒執(zhí)行,只是一個未執(zhí)行的代碼塊,從分區(qū)里讀取build.prop文件
    const auto load_properties_from_partition = [&properties](const std::string& partition,
                                                              int support_legacy_path_until) {
        // 加載{system_ext,product等分區(qū)}/etc/build.prop文件
        // 以后代碼上獲取的屬性值就是從該文件中獲取的
        auto path = "/" + partition + "/etc/build.prop";
        if (load_properties_from_file(path.c_str(), nullptr, &properties)) {
            return;
        }
        ...
    }
    // 獲取第一階段生成的second_stage_resources/system/etc/ramdisk/build.prop
    // 并追加到properties中(這里是map結(jié)構(gòu),注意會覆蓋內(nèi)容)
    LoadPropertiesFromSecondStageRes(&properties);
    // 先讀取的/system/build.prop,然后賦值給properties(這里是map結(jié)構(gòu),注意會覆蓋內(nèi)容)
    load_properties_from_file("/system/build.prop", nullptr, &properties);
    // 獲取/system_ext分區(qū)下的build.prop/default.prop,賦值給然后賦值給propertiess(這里是map結(jié)構(gòu),注意會覆蓋內(nèi)容)
    load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
     // 繼續(xù)讀取的/vendor/default.prop,然后賦值給properties(這里是map結(jié)構(gòu),注意會覆蓋內(nèi)容)
    load_properties_from_file("/vendor/default.prop", nullptr, &properties);
    // }
    // 繼續(xù)讀取的/vendor/build.prop,然后賦值給properties(這里是map結(jié)構(gòu),注意會覆蓋內(nèi)容)
    load_properties_from_file("/vendor/build.prop", nullptr, &properties);
    // 繼續(xù)讀取的/vendor_dlkm/etc/build.prop,然后賦值給properties(這里是map結(jié)構(gòu),注意會覆蓋內(nèi)容)
    load_properties_from_file("/vendor_dlkm/etc/build.prop", nullptr, &properties);
    // 繼續(xù)讀取的/odm_dlkm/etc/build.prop,然后賦值給properties(這里是map結(jié)構(gòu),注意會覆蓋內(nèi)容)
    load_properties_from_file("/odm_dlkm/etc/build.prop", nullptr, &properties);
    // 獲取/{odm,product}.prop/default.prop,賦值給然后賦值給propertiess(這里是map結(jié)構(gòu),注意會覆蓋內(nèi)容)
    load_properties_from_partition("odm", /* support_legacy_path_until */ 28);
    load_properties_from_partition("product", /* support_legacy_path_until */ 30);
    // 因?yàn)閜ropertiess是map結(jié)構(gòu),如果key一樣則會覆蓋內(nèi)容,所以以上代碼的順序不能調(diào)換,優(yōu)先級最高的是/product下的build.prop
    // /system->/system_ext->/vendor->/omd->/product

    // 如果"/debug_ramdisk/adb_debug.prop"存在,說明設(shè)備已經(jīng)unlock過了,則加載unlock過后的屬性值,例如ro.debugger=1,則是開啟了調(diào)試模式
    if (access(kDebugRamdiskProp, R_OK) == 0) {
        LOG(INFO) << "Loading " << kDebugRamdiskProp;
        load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
    }

    // 將從build.prop,default.prop獲取的properties,循環(huán)設(shè)置屬性值
    // 這里是把.prop文件里的屬性值通過mmap映射到內(nèi)存中,使得所有進(jìn)程可以訪問(全局)
    for (const auto& [name, value] : properties) {
        std::string error;
        // 如果是ro則是只讀,只能設(shè)置一次,再次設(shè)置會無效,如果存在相同的key,則會調(diào)用update更新
        if (PropertySet(name, value, &error) != PROP_SUCCESS) {
            LOG(ERROR) << "Could not set '" << name << "' to '" << value
                       << "' while loading .prop files" << error;
        }
    }
    ...
    // 設(shè)置persist.sys.usb.config屬性值來決定是否開啟調(diào)試模式(adb或none)
    update_sys_usb_config();
}

以上代碼主要做的一個動作:讀取各個分區(qū)里的build.prop或直接從指定目錄下讀取build.prop并調(diào)用PropertySet設(shè)置到全局內(nèi)存中,讓所有進(jìn)程訪問

主要的兩個函數(shù):load_properties_from_partitionload_properties_from_file,分別從分區(qū)里讀取和從指定文件讀取,為什么這么做呢?因?yàn)橛械姆謪^(qū)下是沒有/etc目錄的,無法直接指定文件位置,所以通過調(diào)用 "/" + partition + "/etc/build.prop"來讀取,若不存在該文件則直接return。
注意這里讀取了{(lán)system,system_ext,vendor,vendor_dlkm,odm_dlkm,odm,product}/build.prop,由于properties屬于map結(jié)構(gòu),如果key相同是會覆蓋原有的值,所以這里是有優(yōu)先級排序的:

  1. /system/build.prop
  2. /system_ext/{etc}/build.prop
  3. /vendor/default.prop
  4. /vendor/build.prop
  5. /vendor_dlkm/etc/build.prop
  6. /odm_dlkm/etc/build.prop
  7. /odm/{etc}/build.prop
  8. /product/{etc}/build.prop

如果在system中自定義了屬性值,又在product自定義了一樣的屬性值,那么是以product為準(zhǔn)

這里只是init初始化過程,讀取build.prop并解析出來每一行屬性值并調(diào)用PropertySet設(shè)置到全局內(nèi)存中

繼續(xù)分析一下PropertySet函數(shù),該函數(shù)比較簡單

static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
    ...
    // 找到該屬性值
    prop_info* pi = (prop_info*) __system_property_find(name.c_str());
    if (pi != nullptr) {
        // 如果是以ro開頭則代表只讀,禁止寫入,返回error
        if (StartsWith(name, "ro.")) {
            *error = "Read-only property was already set";
            return PROP_ERROR_READ_ONLY_PROPERTY;
        }
        // 若存在該屬性值且非ro 則更新屬性值
        __system_property_update(pi, value.c_str(), valuelen);
    } else {
        // 若找不到該屬性值則新增
        int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
        if (rc < 0) {
            *error = "__system_property_add failed";
            return PROP_ERROR_SET_FAILED;
        }
    }

    // 如果是以persist.開頭,則會全部寫進(jìn)/data/property/persistent_properties
    // 屬于一個緩存機(jī)制
    // std::string persistent_property_filename = "/data/property/persistent_properties";
    if (persistent_properties_loaded && StartsWith(name, "persist.")) {
        WritePersistentProperty(name, value);
    }
    ...
    return PROP_SUCCESS;
}

以上代碼就是若存在屬性值則更新,若不存在則新增。如果是ro開頭的屬性值則代表只讀,只能初始化的時候給默認(rèn)值,后續(xù)不允許修改值,如果是persist開頭的則會全部緩存進(jìn)/data/property/persistent_properties,persist開頭的屬性值是可改的,如果用戶修改過了persist開頭的屬性值相當(dāng)于修改了/data/property/persistent_properties里的屬性值,那么重啟后仍然生效,并不會還原默認(rèn)值。這樣即不影響屬性值的原子性(原有的屬性值),又給了開發(fā)者/用戶操作的空間,如果是刷機(jī)或恢復(fù)出廠則會還原

如果是系統(tǒng)開發(fā)者自定義了屬性值,但是發(fā)現(xiàn)默認(rèn)定義的時候?qū)傩灾禑o法寫入,則可能是property_contexts安全上下文影響,可以直接修改這里的代碼,__system_property_add強(qiáng)行調(diào)用該方法即可

4. SecondStageMain(int argc, char** argv)----->setup 3步驟

setup 4
MountExtraFilesystems();

static void MountExtraFilesystems() {
#define CHECKCALL(x) \
    if ((x) != 0) PLOG(FATAL) << #x " failed.";

    // /apex is used to mount APEXes
    CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=0"));

    // /linkerconfig is used to keep generated linker configuration
    CHECKCALL(mount("tmpfs", "/linkerconfig", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=0"));
#undef CHECKCALL
}

可以看到MountExtraFilesystems主要就掛載了/apex和/linkerconfig并歸屬于tmpfs文件系統(tǒng)(運(yùn)行在內(nèi)存的文件系統(tǒng),運(yùn)行速度較快),這里主要探討一下APEX.
應(yīng)用程序可以通過更新APK來升級,供應(yīng)商客制化系統(tǒng)后可以通過OTA進(jìn)行系統(tǒng)升級,而針對google 開發(fā)者來修復(fù)原生的系統(tǒng)bug該如何更新呢?那就是通過google發(fā)布的安全patch和更新manline以及apex來解決

  • 簡單點(diǎn)說apex為了解決性能而產(chǎn)生的機(jī)制,APK可以通過內(nèi)置升級,但系統(tǒng)升級可是個大問題
  • apex可以將系統(tǒng)內(nèi)部的各個功能打包成模塊,然后針對這些模塊單獨(dú)升級
  • apk是應(yīng)用程序的載體,對應(yīng)用開發(fā)者而言,可以apk方式對應(yīng)用功能進(jìn)行升級
  • apex是系統(tǒng)功能的載體,對系統(tǒng)開發(fā)者(目前看主要是谷歌)而言,可以apex方式對系統(tǒng)功能進(jìn)行升級
  • 一般是google開發(fā)者通過playstore發(fā)布,然后供我們下載更新,而對應(yīng)ODM第三方供應(yīng)商,則需要通過OTA升級
  • apex相當(dāng)于對系統(tǒng)功能進(jìn)行了更細(xì)粒度的劃分,可以獨(dú)立升級這些功能,可以把a(bǔ)pex看成是一個一個的系統(tǒng)升級包

5. SecondStageMain(int argc, char** argv)----->setup 4步驟

	// setup 4
    StartPropertyService(&property_fd);

在setup2中初始化了property,獲取了build.prop和property_contexts并設(shè)置為內(nèi)存中全局共享

void StartPropertyService(int* epoll_socket) {
    // 在init階段version=1,這里已經(jīng)升級到2了
    InitPropertySet("ro.property_service.version", "2");

    // 創(chuàng)建sockets,套接字,可以用于網(wǎng)絡(luò)通信,也可以用于本機(jī)內(nèi)的進(jìn)程通信
    // socketpair()函數(shù)用于創(chuàng)建一對無名的,相互連接的套接字
    // 如果函數(shù)創(chuàng)建成功,則返回0,創(chuàng)建好的套接字分別是sv[0]和sv[1];否則返回-1
    int sockets[2];
    // 參數(shù)1:表示協(xié)議族AF_UNIX
    // 參數(shù)2:表示協(xié)議,SOCK_SEQPACKET提供連續(xù)可靠的數(shù)據(jù)包連接
    // SOCK_CLOEXEC:當(dāng)文件描述符設(shè)置了O_CLOEXEC屬性后,在調(diào)用exec函數(shù)族時,文件描述符就會自動關(guān)閉,無需手動關(guān)閉
    // 而SOCK_DGRAM是基于UDP的
    // 參數(shù)3:表示類型,只能為0
    // 參數(shù)4:套節(jié)字柄,該兩個句柄作用相同,均能進(jìn)行讀寫雙向操作
    if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
        PLOG(FATAL) << "Failed to socketpair() between property_service and init";
    }
    ...
    // PROP_SERVICE_NAME:/dev/socket/property_service,創(chuàng)建socket
    if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                   false, 0666, 0, 0, {});
        result.ok()) {
        property_set_fd = *result;
    } else {
        LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
    }

    // 監(jiān)聽socket:/dev/socket/property_service,最大連接:8
    listen(property_set_fd, 8);

    // 開啟線程處理socket
    auto new_thread = std::thread{PropertyServiceThread};
    property_service_thread.swap(new_thread);
}

這里就是創(chuàng)建了socket,而屬性值服務(wù)的本質(zhì)就是一個socket(/dev/socket/property_service),最大連接為:8,持續(xù)等待連接,連接成功后決定是更新還是新增屬性值,仍然是調(diào)用的PropertySet,繼續(xù)分析一下PropertyServiceThread,該函數(shù)里調(diào)用了handle_property_set_fd函數(shù),來看看具體實(shí)現(xiàn)

static void handle_property_set_fd() {
    // 設(shè)置超時:2s
    static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */

    // 可以設(shè)置四個參數(shù),所以用的是accept4,而當(dāng)啟用了SOCK_CLOEXEC參數(shù)后,進(jìn)程在調(diào)用exec函數(shù)族時,文件描述符就會自動關(guān)閉,無需手動關(guān)閉
    int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
    if (s == -1) {
        return;
    }
    ...
    
    switch (cmd) {
    case PROP_MSG_SETPROP: {
        char prop_name[PROP_NAME_MAX];
        char prop_value[PROP_VALUE_MAX];
        ...
        prop_name[PROP_NAME_MAX-1] = 0;
        prop_value[PROP_VALUE_MAX-1] = 0;
        ...
        // 當(dāng)收到客戶端發(fā)來的請求,去更新或新增屬性值時會調(diào)用HandlePropertySet去處理
        const auto& cr = socket.cred();
        std::string error;
        uint32_t result =
                HandlePropertySet(prop_name, prop_value, source_context, cr, nullptr, &error);
        if (result != PROP_SUCCESS) {
            LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
        }

        break;
      }
 	...
    }
}

可以看到設(shè)置了連接超時時間為2000ms,執(zhí)行 case PROP_MSG_SETPROP,獲取屬性值的名稱和值,調(diào)用HandlePropertySet里的PropertySet進(jìn)行更新屬性值或新增。PropertySet已經(jīng)分析過了

至此屬性值相關(guān)就分析完畢了,簡單的來說就是讀取build.prop屬性值共享到全局內(nèi)存中讓所有進(jìn)程獲取,然后創(chuàng)建屬性值服務(wù)(socket),持續(xù)監(jiān)聽客戶端(哪個進(jìn)程去調(diào)用更新屬性值就是當(dāng)前客戶端)發(fā)來的請求,最大只能同時受理8個來自客戶端的請求,若有客戶端請求則去更新屬性值或新增屬性值

6. SecondStageMain(int argc, char** argv)----->setup 5步驟

	// setup 5
    fs_mgr_vendor_overlay_mount_all();

這里的代碼只要是針對如果有vendor_overlay分區(qū),則覆蓋/vendor分區(qū),主要看供應(yīng)商的客制化

const std::vector<const std::string> kVendorOverlaySourceDirs = {
        "/system/vendor_overlay/",
        "/product/vendor_overlay/",
};

bool fs_mgr_vendor_overlay_mount_all() {
	...
    // 獲取 "/system/vendor_overlay/","/product/vendor_overlay/"下的所有子目錄
    // 將vendor_overlay掛載到vendor上,若存在該覆蓋分區(qū)則會覆蓋之前的vendor分區(qū)
    const auto vendor_overlay_dirs = fs_mgr_get_vendor_overlay_dirs(vndk_version);
    if (vendor_overlay_dirs.empty()) return true;
    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
        LINFO << "vendor overlay: kernel does not support overlayfs";
        return false;
    }

    // Mount each directory in /(system|product)/vendor_overlay/<ver> on /vendor
    // 掛載vendor_overlay到/vendor分區(qū)上
    auto ret = true;
    for (const auto& vendor_overlay_dir : vendor_overlay_dirs) {
        if (!fs_mgr_vendor_overlay_mount(vendor_overlay_dir)) {
            ret = false;
        }
    }
    return ret;
}

7. SecondStageMain(int argc, char** argv)----->setup 6步驟

    // setup 6
    MountHandler mount_handler(&epoll);

該函數(shù)主要功能:持續(xù)監(jiān)控/proc/mounts 節(jié)點(diǎn)(fopen("/proc/mounts", "re")),主要是解析文件中每一行數(shù)據(jù),獲取掛載點(diǎn),掛載分區(qū),文件類型,權(quán)限等, 將解析內(nèi)容生成實(shí)體類追加到要掛載的mounts_中,主要就是對mounts文件解析,更新mounts_中的信息,掛載mounts信息里的分區(qū)
位置:/system/core/init/mount_handler.cpp

MountHandler::MountHandler(Epoll* epoll) : epoll_(epoll), fp_(fopen("/proc/mounts", "re"), fclose) {
    if (!fp_) PLOG(FATAL) << "Could not open /proc/mounts";
    auto result = epoll->RegisterHandler(
            fileno(fp_.get()), [this]() { this->MountHandlerFunction(); }, EPOLLERR | EPOLLPRI);
    if (!result.ok()) LOG(FATAL) << result.error();
}

繼續(xù)看一下MountHandlerFunction函數(shù)

void MountHandler::MountHandlerFunction() {
    rewind(fp_.get());
    std::vector<MountHandlerEntry> touched;
    auto untouched = mounts_; //容器類型
    char* buf = nullptr;
    size_t len = 0;
    // 循環(huán)讀取文件內(nèi)容中的每一行
    while (getline(&buf, &len, fp_.get()) != -1) {
        auto buf_string = std::string(buf);
        // /proc/mounts文件下存在一系列代碼
        // 若讀取到/0/emulated則跳過
        if (buf_string.find("/emulated") != std::string::npos) {
            continue;
        }
        // 根據(jù)讀取的文件內(nèi)容,來解析分區(qū)以及device path,type等
        auto entry = ParseMount(buf_string);
        auto match = untouched.find(entry);
        // 若這一行解析到底了仍然沒有匹配的信息,則這一條記錄追加到touched中
        // entry:舉例--->對文件內(nèi)容/dev/block/dm-33 /data_mirror/data_ce/null ext4 解析過后的實(shí)體
        if (match == untouched.end()) {
            touched.emplace_back(std::move(entry));
        } else {
            // 若找到了匹配的信息則移除
            untouched.erase(match);
        }
    }
    free(buf);
    // 將匹配到的entry進(jìn)行移除,并記錄Mount屬性值
    for (auto& entry : untouched) {
        SetMountProperty(entry, false);
        mounts_.erase(entry);
    }
    // 將未匹配到的entry追加到mounts_,并記錄Mount屬性值
    for (auto& entry : touched) {
        SetMountProperty(entry, true);
        // emplace是更具有性能的 更新或追加
        mounts_.emplace(std::move(entry));
    }
}
  1. 讀取/proc/mounts,解析文件中每一行數(shù)據(jù),獲取掛載點(diǎn),掛載分區(qū),文件類型,權(quán)限等
  2. 若解析到/0/emulated則跳過,不處理
  3. 根據(jù)mounts_,當(dāng)entry(解析后的內(nèi)容)可以在其中找到則移除
  4. 若entry未在mounts_找到,則追加到mounts_中
  5. 相當(dāng)于移除舊的entry,將新的entry追加到mounts_

再看看是如何解析文件內(nèi)容的auto entry = ParseMount(buf_string),解析文件內(nèi)容

MountHandlerEntry ParseMount(const std::string& line) {
    auto fields = android::base::Split(line, " ");
    while (fields.size() < 3) fields.emplace_back("");
    if (fields[0] == "/dev/root") {
        auto& dm = dm::DeviceMapper::Instance();
        std::string path;
        // 根據(jù)名稱獲取system分區(qū)目錄路徑,若根據(jù)名稱找不到則直接獲取根目錄/
        // 若找到根目錄則繼續(xù)找/system,若找到則拿到device path
        // 例如/system就是掛載在/dev/block/dm-1上,那么獲取的就是這個玩意
        // /dev/block/dm-3 /vendor ext4 ro,seclabel,relatime 0 0
        if (dm.GetDmDevicePathByName("system", &path) || dm.GetDmDevicePathByName("vroot", &path)) {
            fields[0] = path;
        } else if (android::fs_mgr::Fstab fstab; android::fs_mgr::ReadDefaultFstab(&fstab)) {
            auto entry = GetEntryForMountPoint(&fstab, "/");
            if (entry || (entry = GetEntryForMountPoint(&fstab, "/system"))) {
                fields[0] = entry->blk_device;
            }
        }
    }
    // 獲取所有/dev目錄下的device
    // readlink 是Linux系統(tǒng)中的一個常用命令,主要用來找出符號鏈接所指向的位置
    // 也就是找到devcie path:/dev/block/dm-33
    if (android::base::StartsWith(fields[0], "/dev/")) {
        if (std::string link; android::base::Readlink(fields[0], &link)) {
            fields[0] = link;
        }
    }
    // fields0:/dev/block/dm-33(blk_device)
    // fields1:掛載在device上的分區(qū)/文件路徑:/data_mirror/cur_profiles(mount_point)
    // fields2:該分區(qū)的type類型,例如可能是ext4(fs_type)
    return MountHandlerEntry(fields[0], fields[1], fields[2]);
}

讓我們再來看看這個文件內(nèi)容
基于Android13的系統(tǒng)啟動流程分析(四)之SecondStageMain階段可以看出來第一列是分區(qū)掛載的位置,第二列是哪個分區(qū),第三列屬于分區(qū)格式

8. SecondStageMain(int argc, char** argv)----->setup 7步驟

	// setup 7
    // 創(chuàng)建ActionManager對象和ServiceList對象
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();
    // 解析rc文件
    LoadBootScripts(am, sm);
  • 加載rc文件,保存到action manager和service list中
  • rc文件中:action 使用 ActionParser,而 service 使用 ServiceParser 解析
  • 主要解析rc中的:service,on,Import,包含了zygote.rc,路徑:/system/bin/app_process64
  • 在文件系統(tǒng)掛載的第一階段,system/vendor分區(qū)已經(jīng)成功掛載,而其它分區(qū)的掛載則通過rc來掛載
  • rc中action的執(zhí)行順序:on early-init,on init,on late-init,on property
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    // 創(chuàng)建解析器,只解析init.rc文件中的service,on,Import類型
    // action 使用 ActionParser,而 service 使用 ServiceParser 解析
    Parser parser = CreateParser(action_manager, service_list);
    // 獲取ro.boot.init_rc屬性值,此時該屬性值應(yīng)該是空的
    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        // 解析/system/core/rootdir/init.rc
        // 這里的路徑就是將/system/core/rootdir/init.rc 拷貝到out目錄下
        parser.ParseConfig("/system/etc/init/hw/init.rc");
        ...
    } else {
        parser.ParseConfig(bootscript);
    }
}

Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser;

    parser.AddSectionParser("service", std::make_unique<ServiceParser>(
                                               &service_list, GetSubcontext(), std::nullopt));
    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));

    return parser;
}

可以看到只需要解析init.rc文件中的service,on,import類型

9. SecondStageMain(int argc, char** argv)----->setup 8步驟

	// setup 8
    // 構(gòu)建action和觸發(fā)器(on early-init),放到event_queue,等待執(zhí)行函數(shù)
   ...
    am.QueueEventTrigger("early-init");
    // 構(gòu)建action和觸發(fā)器(on init),放到event_queue,等待執(zhí)行函數(shù)
    am.QueueEventTrigger("init");
    // 如果是充電模式則不需要掛載文件系統(tǒng)和不要啟動核心服務(wù)
    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");
    }
    // 運(yùn)行所有屬性觸發(fā)器(action),例如 on property
    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");

把a(bǔ)ction加入隊(duì)列中按順序依次執(zhí)行,繼續(xù)一下rc文件中做了什么動作, rc中action的執(zhí)行順序:on early-init,on init,on late-init,on nonencrypted(啟動zygote)on property

9.1. on early-init
on early-init
   ...
    mkdir /acct/uid
	# 掛載linkerconfig(動態(tài)鏈接器)
    mount none /linkerconfig/bootstrap /linkerconfig bind rec
    # 啟動ueventd(位于/system/bin/ueventd),ueventd是init啟動的第一個進(jìn)程
    start ueventd
    # memory.pressure_level used by lmkd
    chown root system /dev/memcg/memory.pressure_level
    chmod 0040 /dev/memcg/memory.pressure_level
    # app mem cgroups, used by activity manager, lmkd and zygote
    mkdir /dev/memcg/apps/ 0755 system system
    mkdir /dev/memcg/system 0550 system system
    mkdir /dev/net 0755 root root
    symlink ../tun /dev/net/tun
    ...
    # 掛載tracefs,可以通過指定方式到處trace日志,分析CPU和內(nèi)存相關(guān)等問題
    mount tracefs tracefs /sys/kernel/tracing gid=3012

    # create sys dirctory
    # 創(chuàng)建/sys目錄并指定權(quán)限
    mkdir /dev/sys 0755 system system
    mkdir /dev/sys/fs 0755 system system
    mkdir /dev/sys/block 0755 system system

可以看到針對lmkd(Low Memory Killer Daemon)以及app 創(chuàng)建用戶組,創(chuàng)建目錄,掛載tracefs:可以通過指定方式到處trace日志,分析CPU和內(nèi)存相關(guān)等問題。
第一個啟動的核心服務(wù)是:start ueventd,位于/system/bin/ueventd,ueventd是init啟動的第一個服務(wù)進(jìn)程

9.2. on init
on init
	...
    chmod 0775 /dev/cpuset/system-background
    chmod 0664 /dev/cpuset/foreground/tasks
    chmod 0664 /dev/cpuset/background/tasks
    chmod 0664 /dev/cpuset/system-background/tasks
    chmod 0664 /dev/cpuset/top-app/tasks
    chmod 0664 /dev/cpuset/restricted/tasks
    chmod 0664 /dev/cpuset/tasks
    chmod 0664 /dev/cpuset/camera-daemon/tasks
    # 掛載bpf
    mount bpf bpf /sys/fs/bpf nodev noexec nosuid
    mkdir /dev/fscklogs 0770 root system    
    ...
    # 允許system組讀寫電源狀態(tài)
    chown system system /sys/power/state
    chown system system /sys/power/wakeup_count
    chmod 0660 /sys/power/state
    ...
    # 在運(yùn)行其他進(jìn)程之前需要先啟動log服務(wù),說明init中啟動的服務(wù),第一個啟動的進(jìn)程是ueventd
    start logd
    # 啟用 Low Memory Killer Daemon(lmkd)
    # 1.基于Memory的CGroup進(jìn)行進(jìn)程的回收;2.作為frameworks與kernel的溝通橋梁傳遞參數(shù)與信息
    # Start lmkd before any other services run so that it can register them
    chown root system /sys/module/lowmemorykiller/parameters/adj
    chmod 0664 /sys/module/lowmemorykiller/parameters/adj
    chown root system /sys/module/lowmemorykiller/parameters/minfree
    chmod 0664 /sys/module/lowmemorykiller/parameters/minfree
    start lmkd

    # Start essential services.
    # 啟用ServiceManager,管理各個服務(wù),非常重要
    start servicemanager
    start hwservicemanager
    start vndservicemanager

可以看到第二個啟動的核心服務(wù)是:start logd,日志系統(tǒng)。
第三個核心服務(wù)是:start lmkd,Low Memory Killer Daemon
作用:基于Memory的CGroup進(jìn)行進(jìn)程的回收,作為frameworks與kernel的溝通橋梁傳遞參數(shù)與信息。
接著啟動了:servicemanager,hwservicemanagervndservicemanager,這些都屬于核心服務(wù)
若核心服務(wù)未啟動成功,那么其他服務(wù)將無法啟動,系統(tǒng)將無法啟動,其他服務(wù)必須依賴核心服務(wù)

9.3. on late-init
# 裝載文件系統(tǒng)并啟動核心系統(tǒng)服務(wù)
on late-init
    trigger early-fs
    # 觸發(fā)on fs和on post-fs
    trigger fs
    trigger post-fs
    trigger late-fs
    trigger post-fs-data
    trigger load_persist_props_action
    trigger load_bpf_programs
    trigger zygote-start
    trigger firmware_mounts_complete
    trigger early-boot
    trigger boot

可以看到調(diào)用順序?yàn)椋簡酉到y(tǒng)on late-init會先執(zhí)行,然后繼續(xù)觸發(fā)on fs,on post-fs ,on late-fs,on zygote-start ,on boot等,在調(diào)用on zygote-start后會解析zygote服務(wù)并指定class 名稱,然后加入服務(wù)管理隊(duì)列,后續(xù)等待調(diào)用on nonencrypted來啟動zygote服務(wù)

9.4. on nonencrypted
on nonencrypted
    class_start main
    class_start late_start

目錄:/system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    # class : 給服務(wù)指定一個類屬
    class main
    priority -20
    # user 在執(zhí)行此服務(wù)之前先切換用戶名。當(dāng)前默認(rèn)為root.
    user root
    # 切換組名
    group root readproc reserved_disk
    # 在/dev/socket/下創(chuàng)建一個socket,并傳遞創(chuàng)建的文件描述符fd給服務(wù)進(jìn)程
    # 其中type必須為dgram或stream,seqpacket.用戶名和組名默認(rèn)為0
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
    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
    # oneshot : 當(dāng)此服務(wù)退出時不會自動重啟.
    # disabled:服務(wù)不會自動運(yùn)行,必須顯式地通過服務(wù)器來啟動
    # 據(jù)設(shè)備相關(guān)的關(guān)鍵服務(wù),如果在4分鐘內(nèi),此服務(wù)重復(fù)啟動了4次,那么設(shè)備將會重啟進(jìn)入還原模式。
    critical window=${zygote.critical_window.minute:-off} target=zygote-fatal

可以看到通過class_start main來啟動主函數(shù)main,位于:frameworks/base/cmds/app_process/app_main.cpp

int main(int argc, char* const argv[])
{
	...
	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.");
    }
}

這里啟動了zygote,并且攜帶參數(shù)啟動了systemServer,關(guān)于zygote這里就不再詳細(xì)分析了

二. 附錄

rc文件中的command以及觸發(fā)器,action等,對應(yīng)的關(guān)系如下:

static const BuiltinFunctionMap builtin_functions = {
        {"bootchart",               {1,     1,    {false,  do_bootchart}}},
        {"chmod",                   {2,     2,    {true,   do_chmod}}},
        {"chown",                   {2,     3,    {true,   do_chown}}},
        {"class_reset",             {1,     1,    {false,  do_class_reset}}},
        {"class_reset_post_data",   {1,     1,    {false,  do_class_reset_post_data}}},
        {"class_restart",           {1,     1,    {false,  do_class_restart}}},
        {"class_start",             {1,     1,    {false,  do_class_start}}},
        {"class_start_post_data",   {1,     1,    {false,  do_class_start_post_data}}},
        {"class_stop",              {1,     1,    {false,  do_class_stop}}},
        {"copy",                    {2,     2,    {true,   do_copy}}},
        {"copy_per_line",           {2,     2,    {true,   do_copy_per_line}}},
        {"domainname",              {1,     1,    {true,   do_domainname}}},
        {"enable",                  {1,     1,    {false,  do_enable}}},
        {"exec",                    {1,     kMax, {false,  do_exec}}},
        {"exec_background",         {1,     kMax, {false,  do_exec_background}}},
        {"exec_start",              {1,     1,    {false,  do_exec_start}}},
        {"export",                  {2,     2,    {false,  do_export}}},
        {"hostname",                {1,     1,    {true,   do_hostname}}},
        {"ifup",                    {1,     1,    {true,   do_ifup}}},
        {"init_user0",              {0,     0,    {false,  do_init_user0}}},
        {"insmod",                  {1,     kMax, {true,   do_insmod}}},
        {"installkey",              {1,     1,    {false,  do_installkey}}},
        {"interface_restart",       {1,     1,    {false,  do_interface_restart}}},
        {"interface_start",         {1,     1,    {false,  do_interface_start}}},
        {"interface_stop",          {1,     1,    {false,  do_interface_stop}}},
        {"load_exports",            {1,     1,    {false,  do_load_exports}}},
        {"load_persist_props",      {0,     0,    {false,  do_load_persist_props}}},
        {"load_system_props",       {0,     0,    {false,  do_load_system_props}}},
        {"loglevel",                {1,     1,    {false,  do_loglevel}}},
        {"mark_post_data",          {0,     0,    {false,  do_mark_post_data}}},
        {"mkdir",                   {1,     6,    {true,   do_mkdir}}},
        // TODO: Do mount operations in vendor_init.
        // mount_all is currently too complex to run in vendor_init as it queues action triggers,
        // imports rc scripts, etc.  It should be simplified and run in vendor_init context.
        // mount and umount are run in the same context as mount_all for symmetry.
        {"mount_all",               {0,     kMax, {false,  do_mount_all}}},
        {"mount",                   {3,     kMax, {false,  do_mount}}},
        {"perform_apex_config",     {0,     0,    {false,  do_perform_apex_config}}},
        {"umount",                  {1,     1,    {false,  do_umount}}},
        {"umount_all",              {0,     1,    {false,  do_umount_all}}},
        {"update_linker_config",    {0,     0,    {false,  do_update_linker_config}}},
        {"readahead",               {1,     2,    {true,   do_readahead}}},
        {"remount_userdata",        {0,     0,    {false,  do_remount_userdata}}},
        {"restart",                 {1,     1,    {false,  do_restart}}},
        {"restorecon",              {1,     kMax, {true,   do_restorecon}}},
        {"restorecon_recursive",    {1,     kMax, {true,   do_restorecon_recursive}}},
        {"rm",                      {1,     1,    {true,   do_rm}}},
        {"rmdir",                   {1,     1,    {true,   do_rmdir}}},
        {"setprop",                 {2,     2,    {true,   do_setprop}}},
        {"setrlimit",               {3,     3,    {false,  do_setrlimit}}},
        {"start",                   {1,     1,    {false,  do_start}}},
        {"stop",                    {1,     1,    {false,  do_stop}}},
        {"swapon_all",              {0,     1,    {false,  do_swapon_all}}},
        {"enter_default_mount_ns",  {0,     0,    {false,  do_enter_default_mount_ns}}},
        {"symlink",                 {2,     2,    {true,   do_symlink}}},
        {"sysclktz",                {1,     1,    {false,  do_sysclktz}}},
        {"trigger",                 {1,     1,    {false,  do_trigger}}},
        {"verity_update_state",     {0,     0,    {false,  do_verity_update_state}}},
        {"wait",                    {1,     2,    {true,   do_wait}}},
        {"wait_for_prop",           {2,     2,    {false,  do_wait_for_prop}}},
        {"write",                   {2,     2,    {true,   do_write}}},
    };

三. 總結(jié)

至此第二階段就分析完畢了,一句話來總結(jié):優(yōu)先保證init進(jìn)程的存活率(拉高優(yōu)先級),處理設(shè)備是否unlock,決定是否卸載一些分區(qū)以及調(diào)整selinux規(guī)則和權(quán)限,創(chuàng)建并啟動屬性服務(wù)(實(shí)質(zhì)上就是把屬性值映射到全局內(nèi)存中供所有進(jìn)程訪問,然后在創(chuàng)建socket等待進(jìn)程來連接 實(shí)現(xiàn)更新和新增屬性值),繼續(xù)決定是否把vendor_overlay覆蓋到/vendor分區(qū)中,然后持續(xù)監(jiān)控/proc/mounts,如果有分區(qū)信息加入到該文件中則掛載此分區(qū),接著解析rc文件(創(chuàng)建目錄,修改權(quán)限,掛載分區(qū),啟動服務(wù)進(jìn)程等),根據(jù)調(diào)用順序啟動核心服務(wù)(adbd,ueventd等)以及主服務(wù)(zygote)和其他服務(wù)文章來源地址http://www.zghlxwxcb.cn/news/detail-423299.html

到了這里,關(guān)于基于Android13的系統(tǒng)啟動流程分析(四)之SecondStageMain階段的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Android 13 網(wǎng)絡(luò) Adb相關(guān)流程深入分析研究

    通過代碼分析發(fā)現(xiàn)Android13 上對 網(wǎng)絡(luò)adb 進(jìn)行了限制! Android13原生代碼要求:必現(xiàn)連接上某個wifi,才能進(jìn)行adb ,并且切換wifi或者關(guān)閉wifi都是會停止adb。 并且Android13 上 wifi adb 端口號每次是變化的,這個也是很不方便的! 如果要做成Android11 或者之前一樣,設(shè)備連接WiFi/有線網(wǎng)

    2024年02月09日
    瀏覽(23)
  • Android SystemServer 啟動流程分析

    Android SystemServer 啟動流程分析

    和你一起終身學(xué) 習(xí),這里是程序員Android 經(jīng)典好文推薦,通過閱讀本文,您將收獲以下知識點(diǎn): 一、SystemServer 啟動的服務(wù)有哪些 二、SystemServer啟動總體流程概述 三、SystemServer 如何啟動,是誰啟動的? 四、 SystemServer 啟動入門 main 方法 五、SystemServer Run 方法初始與啟動 六、

    2024年02月13日
    瀏覽(24)
  • Android 13 驍龍相機(jī)點(diǎn)擊拍照流程分析(二)——點(diǎn)擊拍照到存入相冊

    ? ? ? ? 本篇是在Android 13 驍龍相機(jī)點(diǎn)擊拍照流程分析(一)——點(diǎn)擊拍照到更新到左下角縮略圖文章的基礎(chǔ)上進(jìn)行延申的,前面的預(yù)覽、點(diǎn)擊拍照的過程參考第一篇:Android 13 驍龍相機(jī)點(diǎn)擊拍照流程分析(一)——點(diǎn)擊拍照到更新到左下角縮略圖-CSDN博客 ? ? ? ? 從第一篇的

    2024年02月06日
    瀏覽(20)
  • android framework之Applicataion啟動流程分析

    android framework之Applicataion啟動流程分析

    Application啟動流程框架分析 啟動方式一:通過Launcher啟動app 啟動方式二:在某一個app里啟動第二個app的Activity. 以上兩種方式均可觸發(fā)app進(jìn)程的啟動。但無論哪種方式,最終通過通過調(diào)用AMS的startActivity()來啟動application的。 ?? 根據(jù)上圖分析, 要啟動一個Application,需要涉及五

    2024年02月11日
    瀏覽(25)
  • Android 13 驍龍相機(jī)點(diǎn)擊拍照流程分析(一)——點(diǎn)擊拍照到更新到左下角縮略圖

    Android 13 驍龍相機(jī)點(diǎn)擊拍照流程分析(一)——點(diǎn)擊拍照到更新到左下角縮略圖

    由于最近客戶定制需要將文件掛載類型修改為sdcardfs,由于修改了文件掛載類型,導(dǎo)致了驍龍相機(jī)拍照后不能點(diǎn)擊進(jìn)入相冊,故對驍龍相機(jī)從點(diǎn)擊事件開始進(jìn)行問題的排查,此處不介紹最終的sdcardfs掛載后的問題解決方案 拍照的流程大概分為幾個階段:打開相機(jī)進(jìn)行預(yù)覽、點(diǎn)

    2024年02月04日
    瀏覽(69)
  • Android系統(tǒng)啟動流程概覽

    Boot Rom —— Bootloader —— Linux Kernel —— init進(jìn)程 —— Zygote進(jìn)程(dalvik/ art)—— systemServer —— Apps init 進(jìn)程是Linux系統(tǒng)中,用戶空間啟動的第一個進(jìn)程。 創(chuàng)建并掛載一些文件目錄 啟動屬性服務(wù) 解析 init.rc 配置文件,啟動 Zygote 進(jìn)程 掛載 seLinux 文件目錄,創(chuàng)建seLinux,加載

    2024年02月06日
    瀏覽(22)
  • 筆記:Android 9系統(tǒng)啟動流程

    筆記:Android 9系統(tǒng)啟動流程

    當(dāng)電源鍵按下時,引導(dǎo)芯片代碼(匯編指令)會從預(yù)定的地方(固化在ROM)開始執(zhí)行,將引導(dǎo)程序 BootLoader 加載到 RAM中,然后執(zhí)行 BootLoader 是在 Android 操作系統(tǒng)開始前的一個小程序,主要作用是把系統(tǒng)OS拉起來并運(yùn)行 位置: bootablebootloader 當(dāng) Linux系統(tǒng)被 BootLoader 程序拉起,

    2024年02月14日
    瀏覽(26)
  • Android系統(tǒng)啟動流程 源碼解析

    本文鏈接:https://blog.csdn.net/feather_wch/article/details/132518105 有道云腦圖:https://note.youdao.com/s/GZ9d8vzO 1、整體流程 Boot Room BootLoader idle kthread init init ServiceManager zygote zygote SystemServer app 1、kernel/common/init/main.c 2、andorid.mk-android.bp編譯 3、init是用戶空間鼻祖 屬于C、C++ Framework 1.1 啟動源

    2024年02月11日
    瀏覽(30)
  • Android T 遠(yuǎn)程動畫顯示流程其二——系統(tǒng)側(cè)動畫啟動流程

    Android T 遠(yuǎn)程動畫顯示流程其二——系統(tǒng)側(cè)動畫啟動流程

    接著上篇文章分析 Android T 遠(yuǎn)程動畫顯示流程其一 下面,我們以從桌面點(diǎn)擊一個應(yīng)用啟動的場景來分析遠(yuǎn)程動畫的流程,窗口添加的流程見Android T WMS窗口相關(guān)流程 這里我們從AppTransitionController.handleAppTransitionReady方法開始跟蹤代碼流程 代碼路徑:framework/services/core/java/com/and

    2024年03月28日
    瀏覽(33)
  • Android13音頻子系統(tǒng)分析(一)---整體架構(gòu)

    Android13音頻子系統(tǒng)分析(一)---整體架構(gòu)

    ???????? 目錄 一、應(yīng)用API層 二、Java框架層 三、Native核心層 3.1 AudioFlinger模塊 3.2 AudioPolicyService模塊 四、HAL層 ????????本文基于AOSP13源碼進(jìn)行分析解讀。所以與各個SoC平臺廠商提供的運(yùn)行在真實(shí)設(shè)備上的源碼會有細(xì)微差異,但核心原理區(qū)別不大。 ????????音頻子系

    2024年02月07日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包