前言
由于源码分析的代码量比较大,大部分博客网站的内容显示页面都比较窄,显示出来的效果都异常丑陋,所以您也可以直接查看 《 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)编写的脚本,主要包含五种类型语句: Action
、Command
、Service
、Option
和 Import
,在分析源码的过程中我们会详细介绍。
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 等
!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。