Android-10-系统启动-系列-init天字一号进程

43次阅读

共计 19368 个字符,预计需要花费 49 分钟才能阅读完成。

前言

因为源码剖析的代码量比拟大,大部分博客网站的内容显示页面都比拟窄,显示进去的成果都异样俊俏,所以您也能够间接查看《Thinking in Android》来浏览这边文章,心愿这篇文章能帮你梳理分明 “int 过程原理”

外围源码

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)编写的脚本,次要蕴含五种类型语句:ActionCommandServiceOptionImport,在剖析源码的过程中咱们会具体介绍。

init.rc 的配置代码在:system/core/rootdir/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 等

正文完
 0