“ 天字一号 ” 进程 -- init


# 核心源码

Source Path Source Path
init.rc /system/core/rootdir/init.rc init.cpp /system/core/init/init.cpp
main.cpp /system/core/init/main.cpp first_stage_init.cpp /system/core/init/first_stage_init.cpp
property_service.cpp /system/core/init/property_service.cpp parser.cpp /system/core/init/parser.cpp
log.cpp /system/core/init/log.cpp logging.cpp /system/core/base/logging.cpp
property_service.cpp /system/core/init/property_service.cpp service.cpp /system/core/init/service.cpp
signal_handler.cpp /system/core/init/signal_handler.cpp Action.cpp /system/core/init/Action.cpp
builtins.cpp /system/core/init/builtins.cpp selinux.cpp /system/core/init/selinux.cpp


# 系统启动

1. 按下电源系统启动

当电源按下时引导芯片代码开始从预定义的地方(固化在 ROM)开始执行,加载引导程序 Bootloader 到 RAM,然后执行。

2. 引导程序 Bootloader

引导程序是在 Android 操作系统开始运行前的一个小程序,它的主要作用是把系统 OS 拉起来并运行。

3. linux 内核启动

内核启动时,设置缓存、被保护存储器、计划列表,加载驱动。当内核完成系统设置,它首先在系统文件中寻找 "init" 文件 ,然后启动 root 进程或者系统的第一个进程。

4. 启动 init 进程 (本篇文章要讨论的内容)**

5. 启动 Zygote 进程

创建 JavaVM 并为 JavaVM 注册 JNI,创建服务端 Socket,启动 SystemServer 进程。

6. 启动 SystemServer 进程

启动 Binder 线程池和 SystemServiceManager,并且启动各种系统服务。

7. 启动 Launcher

SystemServer 启动的 ActivityManagerService 会负责启动 Launcher,Launcher 启动后会将已安装应用的快捷图标显示到界面上。


# 关于 init

init 进程,它是一个由 内核启动的用户级进程 ,当 Linux 内核启动之后,运行的 第一个进程 是 init,这个进程是一个守护进程,确切的说,它是 Linux 系统中用户控件的第一个进程,所以它的进程号是 1

它的生命周期贯穿整个 linux 内核运行的始终, linux 中所有其它的进程的共同始祖均为 init 进程。

Android 10 对 init 的入口函数做了调整,不再是之前的 system/core/init/init.cpp,变成了 system/core/init/main.cpp 中的 main() 函数,这么做的目的就是把各阶段的工作分离开,代码逻辑更加简洁明了。

init 入口函数 - - main.cpp

// system/core/init/main.cpp

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

    // Boost prio which will be restored later
    setpriority(PRIO_PROCESS, 0, -20);

    if (!strcmp(basename(argv[0]), "ueventd")) {    // 根据参数,判断是否需要启动 ueventd
        return ueventd_main(argc, argv);    // ueventd 主要是负责设备节点的创建、权限设定等一些列工作
    }

    // 当传入的参数个数大于 1 时,执行下面的几个操作
    if (argc > 1) {
        if (!strcmp(argv[1], "subcontext")) {    // 参数为 subcontext,初始化日志系统
            android::base::InitLogging(argv, &android::base::KernelLogger);
            const BuiltinFunctionMap function_map;

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

        if (!strcmp(argv[1], "selinux_setup")) {    // 参数为 "selinux_setup",启动 Selinux 安全策略
            return SetupSelinux(argv);
        }

        if (!strcmp(argv[1], "second_stage")) {    // 参数为 "second_stage",启动 init 进程第二阶段
            return SecondStageMain(argc, argv);
        }
    }

    // 默认启动 init 进程第一阶段
    return FirstStageMain(argc, argv);
}


一、init 进程 - - 第一阶段

init 进程第一阶段的代码在:system/core/init/first_stage_init.cpp,接下来正式开始我们的分析。

1.1 创建并挂载相关的文件系统

// system/core/init/first_stage_init.cpp

int FirstStageMain(int argc, char** argv) {
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();    // init crash 时重启引导加载程序
    }

    // 用于记录启动时间
    boot_clock::time_point start_time = boot_clock::now();

    // Clear the umask.
    umask(0);

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

    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));    // 挂载 tmpfs 文件系统
    CHECKCALL(mkdir("/dev/pts", 0755));
    CHECKCALL(mkdir("/dev/socket", 0755));
    CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));    // 挂载 devpts 文件系统

#define MAKE_STR(x) __STRING(x)
    // 挂载 proc 文件系统
    CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR

    CHECKCALL(chmod("/proc/cmdline", 0440));    // 8.0 新增, 收紧了 cmdline 目录的权限
    gid_t groups[] = {AID_READPROC};    // 8.0 新增,增加了个用户组
    CHECKCALL(setgroups(arraysize(groups), groups));
    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));    // 挂载 sysfs 文件系统
    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));    // 8.0 新增

    // 提前创建了 kmsg 设备节点文件,用于输出 log 信息
    CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));

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

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

    CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
    CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));

    CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=1000"));
    CHECKCALL(mkdir("/mnt/vendor", 0755));    // 创建可供读写的 vendor 目录
    CHECKCALL(mkdir("/mnt/product", 0755));    // 创建可供读写的 product 目录

    CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=0"));
#undef CHECKCALL

    ... ...
}

该部分主要用于创建和挂载启动所需的文件目录。需要注意的是,在编译 Android 系统源码时,在生成的根文件系统中,并不存在这些目录,它们是系统运行时的目录,即当系统终止时,就会消失。

四类文件系统:(简单理解即可)

tmpfs:一种虚拟内存文件系统,它会将所有的文件存储在虚拟内存中,如果你将 tmpfs 文件系统卸载后,那么其下的所有的内容将不复存在。tmpfs 既可以使用 RAM,也可以使用交换分区,会根据你的实际需要而改变大小。tmpfs 的速度非常惊人,毕竟它是驻留在 RAM 中的,即使用了交换分区,性能仍然非常卓越。由于 tmpfs 是驻留在 RAM 的,因此它的内容是不持久的。断电后,tmpfs 的内容就消失了,这也是被称作 tmpfs 的根本原因。

devpts:为伪终端提供了一个标准接口,它的标准挂接点是 /dev/pts。只要 pty 的主复合设备 /dev/ptmx 被打开,就会在 /dev/pts 下动态的创建一个新的 pty 设备文件。

proc:一个非常重要的虚拟文件系统,它可以看作是内核内部数据结构的接口,通过它我们可以获得系统的信息,同时也能够在运行时修改特定的内核参数。

sysfs:与 proc 文件系统类似,也是一个不占有任何磁盘空间的虚拟文件系统。它通常被挂接在 /sys 目录下。sysfs 文件系统是 Linux2.6 内核引入的,它把连接在系统上的设备和总线组织成一个分级的文件,使得它们可以在用户空间存取。

1.2 重定向输入输出/内核 Log 系统

// system/core/init/first_stage_init.cpp

int FirstStageMain(int argc, char** argv) {
    /* 01. 创建并挂载相关的文件系统 */
    --- --- --- --- --- --- --- --- ---
    SetStdioToDevNull(argv);    // 把标准输入、标准输出和标准错误重定向到空设备文件"/dev/null"
    InitKernelLogging(argv);    // 初始化 kernel Log 系统,供用户打印 log

    if (!errors.empty()) {
        for (const auto& [error_string, error_errno] : errors) {
            LOG(ERROR) << error_string << " " << strerror(error_errno);
        }
        LOG(FATAL) << "Init encountered errors starting first stage, aborting";
    }

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

    ... ...
}

1.3 挂载特定分区设备

// system/core/init/first_stage_init.cpp

int FirstStageMain(int argc, char** argv) {
    /* 01. 创建并挂载相关的文件系统 */
    /* 02. 重定向输入输出/内核 Log 系统 */
    --- --- --- --- --- --- --- --- ---
    // 挂载特定的分区设备
    if (!DoFirstStageMount()) {
        LOG(FATAL) << "Failed to mount required partitions early ...";
    }

    ... ...
}

1.4 完成 SELinux 相关工作

// system/core/init/first_stage_init.cpp

int FirstStageMain(int argc, char** argv) {
    /* 01. 创建并挂载相关的文件系统 */
    /* 02. 重定向输入输出/内核 Log 系统 */
    /* 03. 挂载特定分区设备 */
    --- --- --- --- --- --- --- --- ---
    struct stat new_root_info;
    if (stat("/", &new_root_info) != 0) {
        PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
        old_root_dir.reset();
    }

    if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {
        FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
    }

    // 此处应该是初始化安全框架:Android Verified Boot,AVB 主要用于防止系统文件本身被篡改,
    // 还包含了防止系统回滚的功能,以免有人试图回滚系统并利用以前的漏洞
    SetInitAvbVersionInRecovery();

    setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),
           1);

    // 启动 init 进程,传入参数 selinux_steup
    // 执行命令: /system/bin/init selinux_setup
    const char* path = "/system/bin/init";
    const char* args[] = {path, "selinux_setup", nullptr};
    auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
    dup2(fd, STDOUT_FILENO);
    dup2(fd, STDERR_FILENO);
    close(fd);
    execv(path, const_cast<char**>(args));

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

    return 1;
}


二、init 进程 - - 第二阶段

2.1 初始化属性域

// system/core/init/init.cpp

int SecondStageMain(int argc, char** argv) {
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();  // init crash 时重启引导加载程序
    }

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

    trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };

    SetStdioToDevNull(argv);                 // 把标准输入、标准输出和标准错误重定向到空设备文件"/dev/null"
    InitSecondStageLogging(argv);            // 初始化 /kernel Log 系统
    LOG(INFO) << "init second stage started!";
    ... ...

    keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);    // 调用 syscall,设置安全相关的值

    // 创建 /dev/.booting 文件,就是个标记,表示 booting 进行中
    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
    ... ...

    property_init();    // 初始化属性域 --> 定义于system/core/init/property_service.cpp

    ... ...
}

这部分代码主要的工作应该就是调用 property_init() 初始化属性域,然后设置各种属性。

在 Android 平台中,为了让运行中的所有进程共享系统运行时所需要的各种设置值,系统开辟了属性存储区域,并提供了访问该区域的 API。

2.2 清空环境变量

// system/core/init/init.cpp

int SecondStageMain(int argc, char** argv) {
    /* 01. 初始化属性域 */
    --- --- --- --- --- --- --- --- ---
    // 清除掉之前存储到系统属性中的环境变量
    // Clean up our environment.
    unsetenv("INIT_STARTED_AT");
    unsetenv("INIT_SELINUX_TOOK");
    unsetenv("INIT_AVB_VERSION");
    unsetenv("INIT_FORCE_DEBUGGABLE");

    ... ...
}

2.3 完成 SELinux 相关工作

// system/core/init/init.cpp

int SecondStageMain(int argc, char** argv) {
    /* 01. 初始化属性域 */
    /* 02. 清空环境变量 */
    --- --- --- --- --- --- --- --- ---
    SelinuxSetupKernelLogging();    // 完成第二阶段 selinux 相关的工作
    SelabelInitialize();
    SelinuxRestoreContext();        // 恢复一些文件安全上下文

    ... ...
}

我们发现在 init 进程的第一阶段,也调用了 selinux_initialize() 函数,那么两者有什么区别?

init 进程 第一阶段 主要 加载 selinux 相关的策略,而 第二阶段 调用 SelabelInitialize() 是为了 注册一些处理器

2.4 创建 epoll 句柄

// system/core/init/init.cpp

int SecondStageMain(int argc, char** argv) {
    /* 01. 初始化属性域 */
    /* 02. 清空环境变量 */
    /* 03. 完成 SELinux 相关工作 */
    --- --- --- --- --- --- --- --- ---
    Epoll epoll;
    if (auto result = epoll.Open(); !result.ok()) {    // 新建 epoll 并初始化子进程终止信号处理函数
        PLOG(FATAL) << result.error();
    }

    ... ...
}

2.5 装载子进程信号处理器

// system/core/init/init.cpp

int SecondStageMain(int argc, char** argv) {
    /* 01. 初始化属性域 */
    /* 02. 清空环境变量 */
    /* 03. 完成 SELinux 相关工作 */
    /* 04. 创建 epoll 句柄 */
    --- --- --- --- --- --- --- --- ---
    // 主要是创建 handler 处理子进程终止信号,注册一个 signal 到 epoll 进行监听
    InstallSignalFdHandler(&epoll);
    InstallInitNotifier(&epoll);
    StartPropertyService(&property_fd);

    ... ...
}

init 是一个守护进程,为了防止 init 的子进程成为僵尸进程(zombie process),需要 init 在子进程结束时获取子进程的结束码,通过结束码将程序表中的子进程移除,防止成为僵尸进程的子进程占用程序表的空间(程序表的空间达到上限时,系统就不能再启动新的进程了,会引起严重的系统问题)。

在 linux 当中,父进程 是通过捕捉 SIGCHLD 信号来得知子进程运行结束的情况,此处 init 进程调用 InstallSignalFdHandler() 函数的目的就是 捕获子进程结束的信号

2.6 启动属性服务

// system/core/init/init.cpp

int SecondStageMain(int argc, char** argv) {
    /* 01. 初始化属性域 */
    /* 02. 清空环境变量 */
    /* 03. 完成 SELinux 相关工作 */
    /* 04. 创建 epoll 句柄 */
    /* 05. 装载子进程信号处理器 */
    --- --- --- --- --- --- --- --- ---
    // 进程调用 property_load_boot_defaults 进行默认属性配置相关的工作
    property_load_boot_defaults(load_debug_prop);

    UmountDebugRamdisk();
    fs_mgr_vendor_overlay_mount_all();
    export_oem_lock_status();        // 最终决定 "ro.boot.flash.locked" 的值

    StartPropertyService(&epoll);    // 设置其他系统属性并开启系统属性服务
    MountHandler mount_handler(&epoll);

    // 为 USB 存储设置 udc Contorller, sys/class/udc
    set_usb_controller();

    ... ...
}

2.7 匹配命令和函数之间对应关系

// system/core/init/init.cpp

int SecondStageMain(int argc, char** argv) {
    /* 01. 初始化属性域 */
    /* 02. 清空环境变量 */
    /* 03. 完成 SELinux 相关工作 */
    /* 04. 创建 epoll 句柄 */
    /* 05. 装载子进程信号处理器 */
    /* 06. 启动属性服务*/
    --- --- --- --- --- --- --- --- ---
    const BuiltinFunctionMap function_map;      // 匹配命令和函数之间对应关系
    Action::set_function_map(&function_map);    // 在 Action 中保存 function_map 对象,记录了命令与函数之间的对应关系

    if (!SetupMountNamespaces()) {
        PLOG(FATAL) << "SetupMountNamespaces failed";
    }

    ... ...
}

至此,init 进程的 准备工作 执行完毕, 接下来就要开始 解析 init.rc 文件 的工作。


三、init.rc

3.1 解析 init.rc

// system/core/init/init.cpp

int SecondStageMain(int argc, char** argv) {
    /* 01. 初始化属性域 */
    /* 02. 清空环境变量 */
    /* 03. 完成 SELinux 相关工作 */
    /* 04. 创建 epoll 句柄 */
    /* 05. 装载子进程信号处理器 */
    /* 06. 启动属性服务*/
    /* 07. 匹配命令和函数之间对应关系 */
    --- --- --- --- --- --- --- --- ---
    subcontexts = InitializeSubcontexts();

    // 解析 init.rc 等文件,建立 rc 文件的 action 、service,启动其他进程
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();

    LoadBootScripts(am, sm);    // 解析 init.rc,重点分析

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

    ... ...
}

3.2 LoadBootScripts()

// system/core/init/init.cpp

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);    // 创建解析器 parser

    std::string bootscript = GetProperty("ro.boot.init_rc", "");   // 判断是否存在 bootscript

    if (bootscript.empty()) {              // bootscript 不存在,则解析 init.rc 文件
        parser.ParseConfig("/init.rc");    // 开始实际的解析过程
        if (!parser.ParseConfig("/system/etc/init")) {
            late_import_paths.emplace_back("/system/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
        if (!parser.ParseConfig("/product_services/etc/init")) {
            late_import_paths.emplace_back("/product_services/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}

如果没有定义 bootScript,那么 init 进程会解析 init.rc 文件。init.rc 文件 是在 init 进程启动后执行的启动脚本,文件中记录着 init 进程需执行的操作。

此处解析函数传入的参数为 /init.rc,解析的是与 init 进程 同在根目录下的 init.rc 文件。该文件在编译前,定义于 system/core/rootdir/init.rc 中。

继续往下分析 main 函数之前;我们先了解一下 init.rc 是什么,然后分析 parser 如何解析 init.rc !

3.3 init.rc 配置文件

init.rc 是一个配置文件,内部由 Android 初始化语言(Android Init Language)编写的脚本,主要包含五种类型语句: Action、Command、Service、OptionImport,在分析源码的过程中我们会详细介绍。

init.rc 的配置代码在:system/core/rootdir/init.rc 中。

init.rc

init.rc 文件大致分为两大部分:

一部分是以 “on” 关键字开头的 “动作列表”(action list)

on early-init                            // Action 类型语句
    # Disable sysrq from keyboard        // #:注释符号
    write /proc/1/oom_score_adj -1000

    # Set the security context of /adb_keys if present.
    restorecon /adb_keys
    ... ...

    # cgroup for system_server and surfaceflinger
    mkdir /dev/memcg/system 0550 system system

    start ueventd

    exec_start apexd-bootstrap

Action 类型语句格式:

on <trigger> [&& <trigger>]     // 设置触发器
   <command>
   <command>                    // 动作触发之后要执行的命令
   ...

另一部分是以 “service” 关键字开头的 “服务列表”(service list)

service ueventd /system/bin/ueventd    // Service类型语句
    class core
    critical
    seclabel u:r:ueventd:s0
    shutdown critical

Service 类型语句格式:

service <name> <pathname> [ <argument> ]    // <service的名字><执行程序路径><传递参数>
   <option>                                 // option 是 service 的修饰词,影响什么时候、如何启动 services
   <option>
   ...

借助系统环境变量或 Linux 命令:

       1、动作列表用于 创建所需目录,以及为某些特定文件指定权限

       2、服务列表用来 记录 init 进程需要启动的一些子进程

3.4 LoadBootScripts

回到 LoadBootScripts() 中,实际解析 init.rc 的源码如下( 回顾 ):

// system/core/init/init.cpp

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);     // 创建解析器 parser

    std::string bootscript = GetProperty("ro.boot.init_rc", "");    // 判断是否存在 bootscript

    if (bootscript.empty()) {              // bootscript 不存在,则解析 init.rc 文件
        parser.ParseConfig("/init.rc");    // 开始实际的解析过程
        ... ...

    }
}

3.5 CreateParser()

// system/core/init/init.cpp

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

    // 添加一个 ServiceParser,解析 service
    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
    // 添加一个 ActionParser,解析 on
    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
    // 添加一个 ImportParser,解析 import
    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));

    return parser;
}

初始化 ServiceParser 用来解析 service 块,ActionParser 用来解析 on 块,ImportParser 用来解析 import 块。

3.6 Parser.ParseConfig()

接下来我们重点看下 ParseConfig() 函数:

// system/core/init/parser.cpp

bool Parser::ParseConfig(const std::string& path) {
    if (is_dir(path.c_str())) {        // 判断传入参数是否为目录地址
        return ParseConfigDir(path);   // 递归目录,最终仍是调用 ParseConfigFile 来解析实际的文件
    }
    return ParseConfigFile(path);      // 传入参数为文件地址
}

3.7 ParseConfigDir()

// system/core/init/parser.cpp

bool Parser::ParseConfigDir(const std::string& path) {
    LOG(INFO) << "Parsing directory " << path << "...";
    std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
    if (!config_dir) {
        PLOG(INFO) << "Could not import directory '" << path << "'";
        return false;
    }
    dirent* current_file;    // 递归目录,得到需要处理的文件
    std::vector<std::string> files;
    while ((current_file = readdir(config_dir.get()))) {
        // Ignore directories and only process regular files.
        if (current_file->d_type == DT_REG) {
            std::string current_path =
                android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
            files.emplace_back(current_path);
        }
    }
    // Sort first so we load files in a consistent order (bug 31996208)
    std::sort(files.begin(), files.end());
    for (const auto& file : files) {
        if (!ParseConfigFile(file)) {    // 这里可以发现,最终仍是调用了 ParseConfigFile() 函数
            LOG(ERROR) << "could not import file '" << file << "'";
        }
    }
    return true;
}

3.8 ParseConfigFile()

// system/core/init/parser.cpp

bool Parser::ParseConfigFile(const std::string& path) {
    LOG(INFO) << "Parsing file " << path << "...";
    android::base::Timer t;
    auto config_contents = ReadFile(path);    // 读取路径指定文件中的内容,保存为字符串形式
    if (!config_contents) {
        LOG(INFO) << "Unable to read config file '" << path << "': " << config_contents.error();
        return false;
    }

    ParseData(path, &config_contents.value());    // 解析获取的字符串

    LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
    return true;
}

可以发现,ParseConfigFile() 只是读取文件的内容并转换为字符串,实际的解析工作由 ParseData() 函数完成!

3.9 ParseData()

// system/core/init/parser.cpp

void Parser::ParseData(const std::string& filename, std::string* data) {
    data->push_back('\n');  // TODO: fix tokenizer
    data->push_back('\0');

    // 解析用的结构体
    parse_state state;
    state.line = 0;
    state.ptr = data->data();
    state.nexttoken = 0;
    ... ...

    for (;;) {
        // next_token 以行为单位分割参数传递过来的字符串,初始没有分割符时,最先走到 T_TEXT 分支
        switch (next_token(&state)) {
            case T_EOF:
                end_section();     // 解析结束

                for (const auto& [section_name, section_parser] : section_parsers_) {
                    section_parser->EndFile();
                }

                return;
            case T_NEWLINE:
                state.line++;
                if (args.empty()) break;
                ... ...

                if (line_callback != line_callbacks_.end()) {
                    ... ...
                } else if (section_parsers_.count(args[0])) {
                    // 在前文创建 parser 时,我们为 service,on,import 定义了对应的 parser
                    // 这里就是根据第一个参数,判断是否有对应的 parser
                    end_section();    // 结束上一个 parser 的工作
                    // 获取参数对应的 parser
                    section_parser = section_parsers_[args[0]].get();
                    section_start_line = state.line;
                    // 调用实际 parser 的 ParseSection 函数
                    if (auto result = section_parser
                                      ->ParseSection(std::move(args), filename, state.line);
                        !result) {
                        parse_error_count_++;
                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                        section_parser = nullptr;
                        bad_section_found = true;
                    }
                } else if (section_parser) {
                    /* 
                     * 如果第一个参数不是 service,on,import
                     * 则调用前一个 parser 的 ParseLineSection 函数
                     * 这里相当于解析一个参数块的子项
                     */
                    if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
                        !result) {
                        parse_error_count_++;
                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                    }
                }
                args.clear();    // 清空本次解析的数据
                break;
            case T_TEXT:
                args.emplace_back(state.text);    // 将本次解析的内容写入到 args 中
                break;
        }
    }
}

上面的代码看起来比较复杂,但实际上就是面向对象,根据不同的 关键字,使用不同的 parser 对象 进行解析。

至此,init.rc 文件的解析分析完成!


四、init 进程第二阶段剩余工作

4.1 向执行队列中添加其他 action

// system/core/init/init.cpp

int SecondStageMain(int argc, char** argv) {
    /* 01. 初始化属性域 */
    /* 02. 清空环境变量 */
    /* 03. 完成 SELinux 相关工作 */
    /* 04. 创建 epoll 句柄 */
    /* 05. 装载子进程信号处理器 */
    /* 06. 启动属性服务*/
    /* 07. 匹配命令和函数之间对应关系 */
    /* 08. 解析 init.rc 文件 */
    --- --- --- --- --- --- --- --- ---
    am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");

    // init 执行命令触发器主要分为 early-init,init,late-init,boot 等
    // 添加触发器 early-init,执行 "on early-init" 内容
    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");
    ... ...

    // 添加触发器 init,执行 "on init" 内容,主要包括创建/挂在一些目录,以及 symlink 等
    am.QueueEventTrigger("init");

    // Starting the BoringSSL self test, for NIAP certification compliance.
    am.QueueBuiltinAction(StartBoringSslSelfTest, "StartBoringSslSelfTest");

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

    // Initialize binder before bringing up other system services
    am.QueueBuiltinAction(InitBinder, "InitBinder");

    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");     // on charger阶段
    } else {
        am.QueueEventTrigger("late-init");   // 非充电模式添加触发器 last-init
    }

    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");

    ... ...
}

4.2 处理其他工作

// system/core/init/init.cpp

int SecondStageMain(int argc, char** argv) {
    /* 01. 初始化属性域 */
    /* 02. 清空环境变量 */
    /* 03. 完成 SELinux 相关工作 */
    /* 04. 创建 epoll 句柄 */
    /* 05. 装载子进程信号处理器 */
    /* 06. 启动属性服务*/
    /* 07. 匹配命令和函数之间对应关系 */
    /* 08. 解析 init.rc 文件 */
    /* 09. 向执行队列中添加其他 action */
    --- --- --- --- --- --- --- --- ---
    while (true) {    // 判断是否有事件需要处理
        // By default, sleep until something happens.
        auto epoll_timeout = std::optional<std::chrono::milliseconds>{};

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

        if (!(waiting_for_prop || Service::is_exec_service_running())) {
            // 依次执行每个 action 中携带 command 对应的执行函数
            am.ExecuteOneCommand();
        }
        if (!(waiting_for_prop || Service::is_exec_service_running())) {
            if (!shutting_down) {
                auto next_process_action_time = HandleProcessActions();

                // If there's a process that needs restarting, wake up in time for that.
                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;
                }
            }

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

        // 循环等待事件发生
        if (auto result = epoll.Wait(epoll_timeout); !result) {
            LOG(ERROR) << result.error();
        }
    }
    return 0;
}

init 进程已经启动完成,一些重要的服务都启动完成,并启动了 zygote(/system/bin/app_process64)进程,zygote 初始化时会创建虚拟机,启动 systemserver 等