Android啟動流程之一 Init進(jìn)程啟動

基于Android 9.0代碼進(jìn)行剖析,細(xì)解init進(jìn)程啟動的各個(gè)環(huán)節(jié)內(nèi)容

涉及到的源碼文件


system/core/init/init.cpp

system/core/init/init_first_stage.cpp

system/core/ueventd.cpp

system/core/init/selinux.cpp

system/core/init/sigchld_handler.cpp

system/core/init/property_service.cpp

本文主要的流程如下,init.rc解析及系統(tǒng)核心服務(wù)的啟動請參考:《Android啟動流程之二init.rc解析》 和《Android啟動流程之三 系統(tǒng)核心服務(wù)的啟動》

大致流程圖

一、入口

init進(jìn)程是Linux系統(tǒng)中用戶空間的第一個(gè)進(jìn)程,PID=1,內(nèi)核啟動后會啟動init進(jìn)程并調(diào)用main方法作為入口,執(zhí)行init進(jìn)程應(yīng)該要履行的職責(zé)

  • 掛載文件

  • 解析系統(tǒng)配置的rc文件,執(zhí)行rc文件中的配置項(xiàng)

  • 處理子進(jìn)程的終止信號

  • 提供屬性服務(wù)

  • 啟動zygote進(jìn)程

1.1 init主入口,main函數(shù)

源碼路徑


system/core/init/init.cpp

哇,這個(gè)main函數(shù)有點(diǎn)長,200多行??


int main(int argc, char** argv) {

    //如果文件名是ueventd,執(zhí)行ueventd守護(hù)進(jìn)程的主函數(shù)

    if (!strcmp(basename(argv[0]), "ueventd")) {

        return ueventd_main(argc, argv);

    }

     //如果文件名是ueventd,執(zhí)行watchdogd守護(hù)進(jìn)程的主函數(shù)

    if (!strcmp(basename(argv[0]), "watchdogd")) {

        return watchdogd_main(argc, argv);

    }

   //如果文件名是ueventd,執(zhí)行subcontext守護(hù)進(jìn)程的主函數(shù)

  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();

    }

   //通過讀取INIT_SECOND_STAGE來判斷是否是系統(tǒng)啟動的第一階段

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

    if (is_first_stage) {

        //獲取當(dāng)前啟動時(shí)間

        boot_clock::time_point start_time = boot_clock::now();

        // Clear the umask. 

       //清除umask的設(shè)置,保證新創(chuàng)建的文件訪問權(quán)限不受umask的影響

        // umask是Linux中關(guān)于文件訪問權(quán)限的操作命令,詳情可參考: https://www.cnblogs.com/sench/p/8933638.html

        umask(0);

        clearenv();

        //配置環(huán)境變量

        setenv("PATH", _PATH_DEFPATH, 1);

        // Get the basic filesystem setup we need put together in the initramdisk

        // on / and then we'll let the rc file figure out the rest.

        //掛載tmpfs文件系統(tǒng),關(guān)于tmpfs可參考:https://blog.csdn.net/java211/article/details/52295348/

        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");

       //掛載pts偽終端,pts介紹可以參考:http://blog.chinaunix.net/uid-22746363-id-384008.html

        mkdir("/dev/pts", 0755);

        //掛載socket文件系統(tǒng),Android核心socket存放在這里

        mkdir("/dev/socket", 0755);

        mount("devpts", "/dev/pts", "devpts", 0, NULL);

        #define MAKE_STR(x) __STRING(x)

        //掛載proc文件系統(tǒng),proc是linux中虛擬文件系統(tǒng),可以作為Linux的控制中心,通過修改一些文件來改變

       //內(nèi)核運(yùn)行狀態(tài),里面包括很多l(xiāng)inux核心工具,例如cmdline等,可參考:https://www.cnblogs.com/zydev/p/8728992.html

        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));

        // Don't expose the raw commandline to unprivileged processes.

        chmod("/proc/cmdline", 0440);

        gid_t groups[] = { AID_READPROC };

        setgroups(arraysize(groups), groups);

        //sysfs文件系統(tǒng)是一個(gè)類 似于proc文件系統(tǒng)的特殊文件系統(tǒng),用于將系統(tǒng)中的設(shè)備組織成層次結(jié)構(gòu),

        //并向用戶模式程序提供詳細(xì)的內(nèi)核數(shù)據(jù)結(jié)構(gòu)信息,可參考:https://blog.csdn.net/skyflying2012/article/details/11783847

        mount("sysfs", "/sys", "sysfs", 0, NULL);

        //掛載Selinux

        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);

        //mknod可參考:https://blog.csdn.net/wsclinux/article/details/50907567

        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");

        // /mnt/vendor is used to mount vendor-specific partitions that can not be

        // part of the vendor partition, e.g. because they are mounted read-write.

        mkdir("/mnt/vendor", 0755);

        // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually

        // talk to the outside world...

        //init內(nèi)核log,為什么要使用kernel的log系統(tǒng),因?yàn)榇藭r(shí)Android系統(tǒng)的log還沒有啟動,所以需要使用kernel的log系統(tǒng)。

        InitKernelLogging(argv);

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

        //真正開始啟動第一次初始化

        //在設(shè)備樹中掛載fstab指定的分區(qū),參考:init_first_stage.cpp

        if (!DoFirstStageMount()) {

            LOG(FATAL) << "Failed to mount required partitions early ...";

        }

        //設(shè)置AVB信息到recovery中,參考:init_first_stage.cpp中的:setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);

        SetInitAvbVersionInRecovery();

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

        global_seccomp();

        // Set up SELinux, loading the SELinux policy.

        //初始化SeLinux,參考 1.2節(jié)

        SelinuxSetupKernelLogging();

        SelinuxInitialize();

        // We're in the kernel domain, so re-exec init to transition to the init domain now

        // that the SELinux policy has been loaded.

        //使用加載好的Selinux安全策略,重新設(shè)置init文件的安全權(quán)限

        if (selinux_android_restorecon("/init", 0) == -1) {

            PLOG(FATAL) << "restorecon failed of /init failed";

        }

        //設(shè)置可以進(jìn)入第二階段初始化的標(biāo)記

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

        //記錄第一階段花費(fèi)的時(shí)間

        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 };

        //重新啟動init進(jìn)入main函數(shù)執(zhí)行第二階段

        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";

    }

    //以上代碼執(zhí)行完成第一階段的初始化,下面就會進(jìn)入第二階段的初始化

    // At this point we're in the second stage of init.

    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.

    // 在/dev目錄下創(chuàng)建一個(gè)隱藏的.booting文件表示正在進(jìn)行初始化,初始化完成后會把這個(gè)文件刪除

    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));

    //初始化Android系統(tǒng)屬性,這個(gè)函數(shù)主要是創(chuàng)建一個(gè)共享區(qū)域來存儲屬性值,參考1.3節(jié)

    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.

    //DT的優(yōu)先級高于cmdline,因此需要先執(zhí)行DT的初始化解析,兩者詳細(xì)請參考 1.4節(jié)

    process_kernel_dt();

    process_kernel_cmdline();

    // Propagate the kernel variables to internal variables

    // used by init as well as the current required properties.

    //如官方注釋,這里會讀取內(nèi)核的屬性,參考 1.5 節(jié)

    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);

    // Clean up our environment.

    unsetenv("INIT_SECOND_STAGE");

    unsetenv("INIT_STARTED_AT");

    unsetenv("INIT_SELINUX_TOOK");

    unsetenv("INIT_AVB_VERSION");

    // Selinux第二階段的配置

    SelinuxSetupKernelLogging();

    SelabelInitialize();

    SelinuxRestoreContext();

    // 初始化epoll

    //epoll初始化為分為兩個(gè)階段,我們從函數(shù)命名就可以看得出來,

    // 第一階段在init.cpp的main方法里面signal_handler_init函數(shù)調(diào)用之前,這里如果執(zhí)行失敗的話就直接退出了

    //第二階段就在signal_handler_init初始化系統(tǒng)信號處理器時(shí)會執(zhí)行

    epoll_fd = epoll_create1(EPOLL_CLOEXEC);

    if (epoll_fd == -1) {

        PLOG(FATAL) << "epoll_create1 failed";

    }

    //初始化子進(jìn)程退出信號處理 代碼:system/core/init/sigchld_handler.cpp,請參考 2.1 節(jié)

    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();

    }

    //加載系統(tǒng)xxx.prop:system/core/init/property_service.cpp

    property_load_boot_defaults();

    export_oem_lock_status();

    //start_property_service()函數(shù)創(chuàng)建了socket,然后監(jiān)聽,并且調(diào)用register_epoll_handler()函數(shù)把socket的fd放入epoll中

    start_property_service();

    set_usb_controller();

    const BuiltinFunctionMap function_map;

    Action::set_function_map(&function_map);

    subcontexts = InitializeSubcontexts();

    ActionManager& am = ActionManager::GetInstance();

    ServiceList& sm = ServiceList::GetInstance();

    //加載init.rc,參考 《Android啟動流程之init.rc解析》

    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();

    //以下代碼把init.rc中的action添加到執(zhí)行隊(duì)列中

    //將early-init的Action添加到action_queue中并執(zhí)行觸發(fā)器

    am.QueueEventTrigger("early-init");

    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...

    //等待冷插拔設(shè)備初始化完成

    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");

    //設(shè)備組合鍵的初始化操作

    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", "");

    //如果是充電模式,不要掛載文件系統(tǒng)和啟動核心系統(tǒng)服務(wù)

    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");

    //循環(huán)等待事件的發(fā)生 

    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())) {

            //依次執(zhí)行action中有對應(yīng)command的執(zhí)行函數(shù),參考1.6 節(jié)

            am.ExecuteOneCommand();

        }

        if (!(waiting_for_prop || Service::is_exec_service_running())) {

            if (!shutting_down) {

                 //如果有進(jìn)程需要重啟,執(zhí)行重啟操作,參考 1.7 節(jié)

                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初始化分為兩個(gè)階段,第一個(gè)階段主要是

  • 掛載各種系統(tǒng)核心文件系統(tǒng)

  • 初始化內(nèi)核log

  • 加載系統(tǒng)核心屬性

  • 加載SeLinux安全策略

  • 加載完成Selinux安全策略后重新進(jìn)入init main函數(shù)執(zhí)行第二階段的初始化

1.2 初始化SeLinux

關(guān)于selinux的背景和詳細(xì)信息我們不在這里過多描述,可以參考這里 https://blog.csdn.net/qq_19923217/article/details/81240027

或者是Google官方文檔:https://source.android.com/security/selinux/images/SELinux_Treble.pdf

SelinuxSetupKernelLogging和SelinuxIntialize函數(shù)

  • SelinuxSetupKernelLogging:

system/core/init/selinux.cpp

// This function sets up SELinux logging to be written to kmsg, to match init's logging.

void SelinuxSetupKernelLogging() {

    selinux_callback cb;

    //selinux 打印log所需要用到的callback

    cb.func_log = selinux_klog_callback;

    selinux_set_callback(SELINUX_CB_LOG, cb);

}

SelinuxSetupKernelLogging主要是初始化和設(shè)置SeLinux需要打印log時(shí)用到的callback,log信息會被寫入kmsg

  • SelinuxIntialize

void SelinuxInitialize() {

    //主要用于記錄SeLinux初始化的耗時(shí)

    Timer t;

    LOG(INFO) << "Loading SELinux policy";

    //價(jià)值SeLinux策略

    if (!LoadPolicy()) {

        LOG(FATAL) << "Unable to load SELinux policy";

    }

    //Selinux工作模式包括:

    //1、permissive,這個(gè)模式下所有的操作都被允許,但是如果違反權(quán)限規(guī)則的話會被記錄日志

    //2、enforcing,所有操作都會進(jìn)行權(quán)限檢查

    //以下代碼主要是設(shè)置SeLinux工作模式,我們可以使用adb shell getenforce 來獲取當(dāng)前處于哪種模式

    bool kernel_enforcing = (security_getenforce() == 1);

    bool is_enforcing = IsEnforcing();

    if (kernel_enforcing != is_enforcing) {

        if (security_setenforce(is_enforcing)) {

            PLOG(FATAL) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");

        }

    }

    if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {

        LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();

    }

    // init's first stage can't set properties, so pass the time to the second stage.

    //init的第一階段無法寫入屬性,因此先記錄當(dāng)前耗時(shí),然后傳遞給第二階段

    setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);

}

接下來我們看一下LoadPolicy函數(shù),這會真正的去價(jià)值SeLinux安全策略

LoadPolicy會把安全策略傳遞給kernel,這樣kernel就有了安全策略的配置信息了,這里我們不做過多解釋,SeLinux

加載流程不在本文范圍內(nèi),后面可以通過其他專題來討論


bool LoadPolicy() {

    return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();

}

1.3 property_init

讀取系統(tǒng)屬性配置到共享區(qū)域。


system/core/init/property_service.cpp

void property_init() {

    // 創(chuàng)建dev/_properties_存放系統(tǒng)屬性

    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);

    //解析系統(tǒng)屬性

    CreateSerializedPropertyInfo();

    if (__system_property_area_init()) {

        LOG(FATAL) << "Failed to initialize property area";

    }

    if (!property_info_area.LoadDefaultPath()) {

        LOG(FATAL) << "Failed to load serialized property info file";

    }

}

void CreateSerializedPropertyInfo() {

    auto property_infos = std::vector<PropertyInfoEntry>();

    if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {

        if (!LoadPropertyInfoFromFile("/system/etc/selinux/plat_property_contexts",

                                      &property_infos)) {

            return;

        }

        // Don't check for failure here, so we always have a sane list of properties.

        // E.g. In case of recovery, the vendor partition will not have mounted and we

        // still need the system / platform properties to function.

        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);

        }

    } else {

        if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {

            return;

        }

        if (!LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos)) {

            // Fallback to nonplat_* if vendor_* doesn't exist.

            LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);

        }

    }

    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;

    }

    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);

}

1.4 process dt 和cmdline

process_kernel_dt

該方法主要是打開dt目錄下的素有文件并讀取到系統(tǒng)屬性中,文件定義在/proc/device-tree/firmware/android/目錄下,

先判斷compatible文件中的內(nèi)容是否是android,firmware,如果不是就直接退出了。然后在循環(huán)讀取目錄下的文件,把文件

名作為屬性名,文件內(nèi)容作為屬性值進(jìn)行存儲


system/core/init/init.cpp

static void process_kernel_dt() {

    //如果compatible的文件內(nèi)容不是android,firmware,直接返回

    if (!is_android_dt_value_expected("compatible", "android,firmware")) {

        return;

    }

    //獲取并打開dt文件目錄,默認(rèn)是定義在system/core/init/util.cpp文件中的

    //const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/")

    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);

    if (!dir) return;

    std::string dt_file;

    struct dirent *dp;

    //循環(huán)讀取dt目錄中的文件

    while ((dp = readdir(dir.get())) != NULL) {

        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {

            continue;

        }

        std::string file_name = get_android_dt_dir() + dp->d_name;

        android::base::ReadFileToString(file_name, &dt_file);

        std::replace(dt_file.begin(), dt_file.end(), ',', '.');

        //文件名作為屬性名,文件內(nèi)容作為屬性值

        property_set("ro.boot."s + dp->d_name, dt_file);

    }

}

process_kernel_cmdline函數(shù)

該方法會執(zhí)行兩次import_kernel_cmdline函數(shù),

第一遍主要是判斷是否在qemu中,第一遍第一個(gè)參數(shù)傳遞false,表示不是qemu

第二遍才是真正的讀取proc/cmdline文件中的內(nèi)容并轉(zhuǎn)換為內(nèi)核屬性,第二遍第一個(gè)參數(shù)傳遞true,表示已經(jīng)是qemu,并

傳遞import_kernel_nv函數(shù)指針讀取系統(tǒng)屬性


static void process_kernel_cmdline() {

    // The first pass does the common stuff, and finds if we are in qemu.

    // The second pass is only necessary for qemu to export all kernel params

    // as properties.

    import_kernel_cmdline(false, import_kernel_nv);

    if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);

}

import_kernel_cmdline函數(shù)


system/core/init/util.cpp

void import_kernel_cmdline(bool in_qemu,

                           const std::function<void(const std::string&, const std::string&, bool)>& fn) {

    std::string cmdline;

    //讀取proc/cmdline中的內(nèi)容到cmdline字符串中

    android::base::ReadFileToString("/proc/cmdline", &cmdline);

    //循環(huán)讀取屬性,讀取方式為,將字符串cmdline的內(nèi)容按空格分隔成字符串?dāng)?shù)組,并循環(huán)遍歷

    //這個(gè)數(shù)組,然后調(diào)用fn設(shè)置屬性,這里fn就是在process_kernel_cmdline方法中傳遞進(jìn)來的import_kernel_nv

    for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {

        std::vector<std::string> pieces = android::base::Split(entry, "=");

        if (pieces.size() == 2) {

            fn(pieces[0], pieces[1], in_qemu);

        }

    }

}

import_kernel_nv函數(shù)


system/core/init/init.cpp

static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {

    if (key.empty()) return;

    //import_kernel_cmdline第一次執(zhí)行的時(shí)候傳遞過來的for_emulator為false,第二次為true

    //因此這里在第一遍調(diào)用的時(shí)候不會執(zhí)行

    if (for_emulator) {

        // In the emulator, export any kernel option with the "ro.kernel." prefix.

        property_set("ro.kernel." + key, value);

        return;

    }

    if (key == "qemu") {

        strlcpy(qemu, value.c_str(), sizeof(qemu));

    } else if (android::base::StartsWith(key, "androidboot.")) {

        property_set("ro.boot." + key.substr(12), value);

    }

}

1.5 export_kernel_boot_props


static void export_kernel_boot_props() {

    struct {

        const char *src_prop;

        const char *dst_prop;

        const char *default_value;

    } prop_map[] = {

        { "ro.boot.serialno", "ro.serialno", "", },

        { "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", },

    };

    // unknown作為默認(rèn)值,如果沒有寫入指定的value的話就用unknown作為value

    for (size_t i = 0; i < arraysize(prop_map); i++) {

        std::string value = GetProperty(prop_map[i].src_prop, "");

        property_set(prop_map[i].dst_prop, (!value.empty()) ? value : prop_map[i].default_value);

    }

}

1.6 ExecuteOneCommand


void ActionManager::ExecuteOneCommand() {

    // Loop through the event queue until we have an action to execute

    while (current_executing_actions_.empty() && !event_queue_.empty()) {

        for (const auto& action : actions_) {

            if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },

                           event_queue_.front())) {

                current_executing_actions_.emplace(action.get());

            }

        }

        event_queue_.pop();

    }

    //current_executing_actions_ 為空則退出

    if (current_executing_actions_.empty()) {

        return;

    }

    //獲取current_executin_actions_ 頭部

    auto action = current_executing_actions_.front();

    if (current_command_ == 0) {

        std::string trigger_name = action->BuildTriggersString();

        LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()

                  << ":" << action->line() << ")";

    }

    //system/core/init/action.cpp

    action->ExecuteOneCommand(current_command_);

    // If this was the last command in the current action, then remove

    // the action from the executing list.

    // If this action was oneshot, then also remove it from actions_.

   //如果這是最后一個(gè)action或者是一個(gè)oneshot的action,執(zhí)行完成后直接移除隊(duì)列

    ++current_command_;

    if (current_command_ == action->NumCommands()) {

        current_executing_actions_.pop();

        current_command_ = 0;

        if (action->oneshot()) {

            auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };

            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));

        }

    }

}


void Action::ExecuteOneCommand(std::size_t command) const {

    // We need a copy here since some Command execution may result in

    // changing commands_ vector by importing .rc files through parser

    //為了避免因?yàn)榻馕?rc文件導(dǎo)致command_隊(duì)列被改變,所以要先拷貝一份

    Command cmd = commands_[command];

    //執(zhí)行command

    ExecuteCommand(cmd);

}

void Action::ExecuteCommand(const Command& command) const {

    android::base::Timer t;

   //調(diào)用Command的invokeFunc函數(shù)執(zhí)行

    auto result = command.InvokeFunc(subcontext_);

    auto duration = t.duration();

    // There are many legacy paths in rootdir/init.rc that will virtually never exist on a new

    // device, such as '/sys/class/leds/jogball-backlight/brightness'. As of this writing, there

    // are 198 such failures on bullhead. Instead of spamming the log reporting them, we do not

    // report such failures unless we're running at the DEBUG log level.

    bool report_failure = !result.has_value();

    if (report_failure && android::base::GetMinimumLogSeverity() > android::base::DEBUG &&

        result.error_errno() == ENOENT) {

        report_failure = false;

    }

    // Any action longer than 50ms will be warned to user as slow operation

    if (report_failure || duration > 50ms ||

        android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {

        std::string trigger_name = BuildTriggersString();

        std::string cmd_str = command.BuildCommandString();

        LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_

                  << ":" << command.line() << ") took " << duration.count() << "ms and "

                  << (result ? "succeeded" : "failed: " + result.error_string());

    }

}

1.7 重啟進(jìn)程


static std::optional<boot_clock::time_point> RestartProcesses() {

    std::optional<boot_clock::time_point> next_process_restart_time;

    //遍歷service列表進(jìn)行重啟

    for (const auto& s : ServiceList::GetInstance()) {

        //如果flag不是SVC_RESTARTING,取下一個(gè)

        if (!(s->flags() & SVC_RESTARTING)) continue;

        auto restart_time = s->time_started() + 5s;

        if (boot_clock::now() > restart_time) {

            //啟動進(jìn)程

            if (auto result = s->Start(); !result) {

                LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();

            }

        } else {

            if (!next_process_restart_time || restart_time < *next_process_restart_time) {

                next_process_restart_time = restart_time;

            }

        }

    }

    return next_process_restart_time;

}

二、信號處理

sigchld_handler_init主要是子進(jìn)程死掉后,父進(jìn)程中會接收到一個(gè)SIGCHLD信號,init進(jìn)程接收到這個(gè)信號后會將已經(jīng)死了的子進(jìn)程移除,避免成為

僵尸進(jìn)程占用系統(tǒng)程序表的空間。

2.1 sigchld_handler_init


system/core/init/sigchld_handler.cpp

void sigchld_handler_init() {

    // Create a signalling mechanism for SIGCHLD.

    int s[2];

    //創(chuàng)建用于接收和寫入信號的兩個(gè)socket

    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {

        PLOG(FATAL) << "socketpair failed in sigchld_handler_init";

    }

    signal_write_fd = s[0];

    signal_read_fd = s[1];

    // Write to signal_write_fd if we catch SIGCHLD.

    //接收到SIGCHLD信號后就寫入signal_write_fd

    struct sigaction act;

    memset(&act, 0, sizeof(act));

    //SIGCHLD_handler會被存放在sigaction數(shù)據(jù)結(jié)構(gòu)里面,負(fù)責(zé)處理SIGCHLD信號

    act.sa_handler = SIGCHLD_handler;

    //用于標(biāo)記只有在子進(jìn)程死掉用才接收信息

    act.sa_flags = SA_NOCLDSTOP;

    //注冊將需要監(jiān)聽的信號SIGCHLD及對應(yīng)的處理器

    sigaction(SIGCHLD, &act, 0);

    //進(jìn)入waitid處理子進(jìn)程是否退出的情況

    ReapAnyOutstandingChildren();

    //注冊回調(diào)用于監(jiān)聽信號,其中handle_signal是函數(shù)指針

    register_epoll_handler(signal_read_fd, handle_signal);

}

Linux系統(tǒng)中進(jìn)程之間可以發(fā)送信息來實(shí)現(xiàn)進(jìn)程間通訊,這里的這種機(jī)制可以叫做 信號,每個(gè)進(jìn)程在處理信號時(shí)是需要被注冊為處理者。

這里的兩個(gè)函數(shù)中,SIGCHILD_handler負(fù)責(zé)寫入信號,handle_signal負(fù)責(zé)讀取信號。子進(jìn)程在死掉后會產(chǎn)生SIGCHILD信號并寫入signal_write_fd,信號通過

socket發(fā)送到父進(jìn)程,父進(jìn)程讀取信號到signal_read_fd


//寫入信號

static void SIGCHLD_handler(int) {

    //向signal_write_fd中寫入1

    if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {

        PLOG(ERROR) << "write(signal_write_fd) failed";

    }

}

//讀取信號

static void handle_signal() {

    // Clear outstanding requests.

    char buf[32];

    read(signal_read_fd, buf, sizeof(buf));

    //這里只要是回收死掉的子進(jìn)程

    ReapAnyOutstandingChildren();

}

2.2 ReapAnyOutstandingChildren函數(shù)


void ReapAnyOutstandingChildren() {

    while (ReapOneProcess()) {

    }

}

static bool ReapOneProcess() {

    siginfo_t siginfo = {};

    // This returns a zombie pid or informs us that there are no zombies left to be reaped.

    // It does NOT reap the pid; that is done below.

    if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {

        PLOG(ERROR) << "waitid failed";

        return false;

    }

    auto pid = siginfo.si_pid;

    if (pid == 0) return false;

    // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid

    // whenever the function returns from this point forward.

    // We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we

    // want the pid to remain valid throughout that (and potentially future) usages.

    auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });

    std::string name;

    std::string wait_string;

    Service* service = nullptr;

    if (PropertyChildReap(pid)) {

        name = "Async property child";

    } else if (SubcontextChildReap(pid)) {

        name = "Subcontext";

    } else {

        //find service via pid

        service = ServiceList::GetInstance().FindService(pid, &Service::pid);

        if (service) {

            name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);

            if (service->flags() & SVC_EXEC) {

                auto exec_duration = boot_clock::now() - service->time_started();

                auto exec_duration_ms =

                    std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();

                wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);

            }

        } else {

            name = StringPrintf("Untracked pid %d", pid);

        }

    }

    if (siginfo.si_code == CLD_EXITED) {

        LOG(INFO) << name << " exited with status " << siginfo.si_status << wait_string;

    } else {

        LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;

    }

    if (!service) return true;

    //回收子進(jìn)程,參考system/core/init/service.cpp:: Reap

    service->Reap(siginfo);

    if (service->flags() & SVC_TEMPORARY) {

        ServiceList::GetInstance().RemoveService(*service);

    }

    return true;

}

service.cpp :: Reap


void Service::Reap(const siginfo_t& siginfo) {

    // flag為ONESHOT或者RESTART時(shí)直接kill掉進(jìn)程組

    if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {

        KillProcessGroup(SIGKILL);

    }

    // Remove any descriptor resources we may have created.

    std::for_each(descriptors_.begin(), descriptors_.end(),

                  std::bind(&DescriptorInfo::Clean, std::placeholders::_1));

    for (const auto& f : reap_callbacks_) {

        f(siginfo);

    }

    if (flags_ & SVC_EXEC) UnSetExec();

    if (flags_ & SVC_TEMPORARY) return;

    pid_ = 0;

    flags_ &= (~SVC_RUNNING);

    start_order_ = 0;

    // Oneshot processes go into the disabled state on exit,

    // except when manually restarted.

    if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) {

        flags_ |= SVC_DISABLED;

    }

    // Disabled and reset processes do not get restarted automatically.

    if (flags_ & (SVC_DISABLED | SVC_RESET)) {

        NotifyStateChange("stopped");

        return;

    }

    // If we crash > 4 times in 4 minutes, reboot into recovery.

    boot_clock::time_point now = boot_clock::now();

    if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {

        if (now < time_crashed_ + 4min) {

            if (++crash_count_ > 4) {

                LOG(FATAL) << "critical process '" << name_ << "' exited 4 times in 4 minutes";

            }

        } else {

            time_crashed_ = now;

            crash_count_ = 1;

        }

    }

    flags_ &= (~SVC_RESTART);

    flags_ |= SVC_RESTARTING;

    // Execute all onrestart commands for this service.

    onrestart_.ExecuteAllCommands();

   //設(shè)置對應(yīng)的service為restarting狀態(tài)

    NotifyStateChange("restarting");

    return;

}

參考:

http://www.itdecent.cn/p/464c3d1203b1

http://gityuan.com/2016/02/05/android-init/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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