深入理解init
1.1 概述
init 是一个进程,确切地说,它是Linux 系统中用户空间地第一个进程。由于Android 是基于Linux 内核的,所以init 也是Android 系统中用户空间的第一个进程,它的进程号是1。它的主要指责为:
- init 进程负责创建系统中的几个关键进程,包括zygote。
- Android 系统有很多属性,于是init 就提供了一个property service(属性服务)来管理它们。
1.2 init 分析
init 进程的入口函数是main,它的代码如下所示:
/*
* 1.C++中主函数有两个参数,第一个参数argc表示参数个数,第二个参数是参数列表,也就是具体的参数
* 2.init的main函数有两个其它入口,一是参数中有ueventd,进入ueventd_main,二是参数中有watchdogd,进入watchdogd_main
*/
int main(int argc, char** argv) {
/*
* 1.strcmp是String的一个函数,比较字符串,相等返回0
* 2.C++中0也可以表示false
* 3.basename是C库中的一个函数,得到特定的路径中的最后一个'/'后面的内容,
* 比如/sdcard/miui_recovery/backup,得到的结果是backup
*/
if (!strcmp(basename(argv[0]), "ueventd")) {
//当argv[0]的内容为ueventd时,strcmp的值为0,!strcmp为1
//1表示true,也就执行ueventd_main,ueventd主要是负责设备节点的创建、权限设定等一些列工作
return ueventd_main(argc, argv);
}
if (!strcmp(basename(argv[0]), "watchdogd")) {
//watchdogd俗称看门狗,用于系统出问题时重启系统
return watchdogd_main(argc, argv);
}
if (argc > 1 && !strcmp(argv[1], "subcontext")) {
InitKernelLogging(argv);
const BuiltinFunctionMap function_map;
return SubcontextMain(argc, argv, &function_map);
}
if (REBOOT_BOOTLOADER_ON_PANIC) {
//初始化重启系统的处理信号,内部通过sigaction 注册信号,当监听到该信号时重启系统
InstallRebootSignalHandlers();
}
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr); //查看是否有环境变量INIT_SECOND_STAGE
/*
* init的main方法会执行两次,由is_first_stage控制,first_stage就是第一阶段要做的事
*/
if (is_first_stage) { //只执行一次,因为在方法体中有设置INIT_SECOND_STAGE
boot_clock::time_point start_time = boot_clock::now();
// Clear the umask.
umask(0); //清空文件权限
clearenv();
setenv("PATH", _PATH_DEFPATH, 1); //注册环境变量PATH
// 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.
// 创建一些文件,并挂载设备,这些是与Linux 相关的
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
#define MAKE_STR(x) __STRING(x)
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);
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
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...
// InitKernelLogging 首先是将标准输入输出重定向到"/sys/fs/selinux/null",然后调用InitLogging初始化log日志系统
InitKernelLogging(argv);
LOG(INFO) << "init first stage started!";
if (!DoFirstStageMount()) { // 主要作用是初始化特定设备并挂载
LOG(FATAL) << "Failed to mount required partitions early ...";
}
//Avb即Android Verfied boot,功能包括Secure Boot, verfying boot 和 dm-verity,
//原理都是对二进制文件进行签名,在系统启动时进行认证,确保系统运行的是合法的二进制镜像文件。
//其中认证的范围涵盖:bootloader,boot.img,system.img
SetInitAvbVersionInRecovery(); //在刷机模式下初始化avb的版本,不是刷机模式
// Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
global_seccomp();
// Set up SELinux, loading the SELinux policy.
SelinuxSetupKernelLogging();
SelinuxInitialize(); //加载SELinux policy,也就是安全策略,
// We're in the kernel domain, so re-exec init to transition to the init domain now
// that the SELinux policy has been loaded.
if (selinux_android_restorecon("/init", 0) == -1) { //restorecon命令用来恢复SELinux文件属性即恢复文件的安全上下文
PLOG(FATAL) << "restorecon failed of /init failed";
}
setenv("INIT_SECOND_STAGE", "true", 1);
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 };
execv(path, args); //重新执行main方法,进入第二阶段
// execv() only returns if an error happened, in which case we
// panic and never fall through this conditional.
PLOG(FATAL) << "execv(\"" << path << "\") failed";
}
...
}
在init初始化过程中,Android分别挂载了tmpfs,devpts,proc,sysfs,selinuxfs这5类文件系统。
tmpfs是一种虚拟内存文件系统,它会将所有的文件存储在虚拟内存中,如果你将tmpfs文件系统卸载后,那么其下的所有的内容将不复存在。
tmpfs既可以使用RAM,也可以使用交换分区,会根据你的实际需要而改变大小。
tmpfs的速度非常惊人,毕竟它是驻留在RAM中的,即使用了交换分区,性能仍然非常卓越。
由于tmpfs是驻留在RAM的,因此它的内容是不持久的。断电后,tmpfs的内容就消失了,这也是被称作tmpfs的根本原因。
devpts文件系统为伪终端提供了一个标准接口,它的标准挂接点是/dev/ pts。
只要pty的主复合设备/dev/ptmx被打开,就会在/dev/pts下动态的创建一个新的pty设备文件。
proc文件系统是一个非常重要的虚拟文件系统,它可以看作是内核内部数据结构的接口,通过它我们可以获得系统的信息,同时也能够在运行时修改特定的内核参数。
与proc文件系统类似,sysfs文件系统也是一个不占有任何磁盘空间的虚拟文件系统。
它通常被挂接在/sys目录下。sysfs文件系统是Linux2.6内核引入的,
它把连接在系统上的设备和总线组织成为一个分级的文件,使得它们可以在用户空间存取。
selinuxfs也是虚拟文件系统,通常挂载在/sys/fs/selinux目录下,用来存放SELinux安全策略文件
init进程第一阶段做的主要工作是挂载分区,创建设备节点和一些关键目录,初始化日志输出系统,启用SELinux安全策略
第二阶段
int main(int argc, char** argv) {
...
// 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.
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));//创建 /dev/.booting 文件,就是个标记,表示booting进行中
property_init();//初始化属性系统,并从指定文件读取属性
//接下来的一系列操作都是从各个文件读取一些属性,然后通过property_set设置系统属性
// If arguments are passed both on the command line and in DT,
// properties set in DT always have priority over the command-line ones.
/*
* 1.这句英文的大概意思是,如果参数同时从命令行和DT传过来,DT的优先级总是大于命令行的
* 2.DT即device-tree,中文意思是设备树,这里面记录自己的硬件配置和系统运行参数
*/
process_kernel_dt();//处理DT属性
process_kernel_cmdline();//处理命令行属性
// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
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);
// Set memcg property based on kernel cmdline argument
bool memcg_enabled = android::base::GetBoolProperty("ro.boot.memcg",false);
if (memcg_enabled) {
// root memory control cgroup
mkdir("/dev/memcg", 0700);
chown("/dev/memcg",AID_ROOT,AID_SYSTEM);
mount("none", "/dev/memcg", "cgroup", 0, "memory");
// app mem cgroups, used by activity manager, lmkd and zygote
mkdir("/dev/memcg/apps/",0755);
chown("/dev/memcg/apps/",AID_SYSTEM,AID_SYSTEM);
mkdir("/dev/memcg/system",0550);
chown("/dev/memcg/system",AID_SYSTEM,AID_SYSTEM);
}
// Clean up our environment.
unsetenv("INIT_SECOND_STAGE");
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");
// Now set up SELinux for second stage.
SelinuxSetupKernelLogging(); //第二阶段初始化SELinux policy
SelabelInitialize();
SelinuxRestoreContext(); //恢复安全上下文
epoll_fd = epoll_create1(EPOLL_CLOEXEC); //创建epoll实例,并返回epoll的文件描述符
if (epoll_fd == -1) {
PLOG(FATAL) << "epoll_create1 failed";
}
sigchld_handler_init(); //主要是创建handler处理子进程终止信号,创建一个匿名socket并注册到epoll进行监听
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();
}
property_load_boot_defaults(); //从文件中加载一些属性,读取usb配置
export_oem_lock_status(); //设置ro.boot.flash.locked 属性
start_property_service(); //开启一个socket监听系统属性的设置
set_usb_controller(); //设置sys.usb.controller 属性
...
}
init进程第二阶段主要工作是初始化属性系统,解析SELinux的匹配规则,处理子进程终止信号,启动系统属性服务,可以说每一项都很关键,如果说第一阶段是为属性系统,SELinux做准备,那么第二阶段就是真正去把这些落实的。
init.rc
在该函数里面,还会解析一个叫init.rc 的配置文件。
init.rc是init进程启动的配置脚本,这个脚本是用一种叫Android Init Language(Android初始化语言)的语言写的,在7.0以前,init进程只解析根目录下的init.rc文件,但是随着版本的迭代,init.rc越来越臃肿,所以在7.0以后,init.rc一些业务被分拆到/system/etc/init,/vendor/etc/init,/odm/etc/init三个目录下,
Android Init Language语法定义在platform/system/core/init/README.md
.rc 文件主要配置了两个东西,一个是action,一个是service,trigger 和command 是对action 的补充,options 是对service 的补充。action 加上trigger 以及一些command,组成一个Section,service加上一些option,也组成一个Section,.rc文件就是由一个个Section组成。.rc文件头部有一个import的语法,表示这些.rc也一并包含并解析。
Init .rc Files
/init.rc 是最主要的一个.rc文件,它由init进程在初始化时加载,主要负责系统初始化,它会导入 /init.${ro.hardware}.rc ,这个是系统级核心厂商提供的主要.rc文件。当执行 mount_all 语句时,init进程将加载所有在 /{system,vendor,odm}/etc/init/ 目录下的文件,挂载好文件系统后,这些目录将会为Actions和Services服务。
有一个特殊的目录可能被用来替换上面的三个默认目录,这主要是为了支持工厂模式和其他非标准的启动模式,上面三个目录用于正常的启动过程,这三个用于扩展的目录是:
- /system/etc/init/ 用于系统本身,比如SurfaceFlinger, MediaService, and logcatd.
- /vendor/etc/init/ 用于SoC(系统级核心厂商,如高通),为他们提供一些核心功能和服务
- /odm/etc/init/ 用于设备制造商(odm定制厂商,如华为、小米),为他们的传感器或外围设备提供一些核心功能和服务
1.3 解析.rc 文件
int main(int argc, char** argv) {
...
LoadBootScripts(am, sm);
...
}
// 加载和解析init.rc 文件
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
// 创建解析器
Parser parser = CreateParser(action_manager, service_list);
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
// 解析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("/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);
}
}
主要解析工作是在/system/core/init/parser.cpp
void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
...
//lambda表达式,函数的引用,每次解析新的一行之前
//都会调用这个将上一行解析的结果放到对应的数组中
auto end_section = [&] {
if (section_parser == nullptr) return;
if (auto result = section_parser->EndSection(); !result) {
(*parse_errors)++;
LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
}
//重置
section_parser = nullptr;
section_start_line = -1;
};
...
}
具体解析过程在后面的一个无限for 循环里面:
void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
...
for (;;) {
switch (next_token(&state)) {
case T_EOF:
end_section(); // 这里就是调用上面的end_section
return;
case T_NEWLINE:
state.line++;
if (args.empty()) break;
// If we have a line matching a prefix we recognize, call its callback and unset any
// current section parsers. This is meant for /sys/ and /dev/ line entries for
// uevent.
for (const auto& [prefix, callback] : line_callbacks_) {
if (android::base::StartsWith(args[0], prefix)) {
end_section();
if (auto result = callback(std::move(args)); !result) {
(*parse_errors)++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
break;
}
}
if (section_parsers_.count(args[0])) { //判断是否包含 on service import
end_section();
section_parser = section_parsers_[args[0]].get(); //取出对应的parser
section_start_line = state.line;
if (auto result =
section_parser->ParseSection(std::move(args), filename, state.line);
!result) {
(*parse_errors)++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
section_parser = nullptr;
}
} else if (section_parser) { //不包含 on service import则是command或option
if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
!result) {
(*parse_errors)++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
}
args.clear();
break;
case T_TEXT:
args.emplace_back(state.text);
break;
}
}
}
next_token扫描init.rc中的token
找到其中的文件结束(
EOF
)、文本(TEXT
)、新行(NEWLINE
),其中的空格
、\t
、\r
会被忽略掉;对于TEXT,空格
、\t
、\r
、\n
都是TEXT的结束标志。
section_parsers_.count(args[0])
会判断args[]第一个字符是不是on、service或import
如果是就拿到对应的解析器,例如是service开头的新行,section_parser就是对应之前添加的ServcieParser解析器,并且执行auto result = section_parser-> ParseSection(std::move(args), filename, state.line);
,否则就执行auto result = section_parser-> ParseLineSection(std::move(args), state.line);
。
ParseLineSection是解析service下面附带的option(例如oneshot,class等等),并将这些属性添加到Service对象中)。
解析完一个Service对象后
end_section()
中的section_parser->EndSection()
将Service对象添加到Service List(services_)中,并且重置section_parser = null,为解析下一个Service做准备。
on开头的使用ActionParser解析器解析,该解析器的ParseSection方法主要是生成action对象,并将action对象添加到actions_数组中,ParseLineSection方法主要是解析command对应的函数,然后添加到action对象的commands_数组中。ServiceParser的ParseLineSection是直接执行option对应函数,而ActionParser是将对应函数保存到commands_数组中,当Action触发时,才会依次执行command函数。
总共有三个解析器,分别是:
- ActionParser
定义在platform/system/core/init/action.cpp
- ServiceParser
定义在platform/system/core/init/service.cpp
- ImportParser
定义在platform/system/core/init/import_parser.cpp
init.rc解析大致如此。
1.4 触发条件
经过上一步的解析,系统从各种.rc文件中读取了需要执行的Action和Service,但是还是需要一些额外的配置,也需要加入触发条件准备去触发。
int main(int argc, char** argv) {
...
// 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) parser.DumpState(); //打印一些当前Parser的信息,默认是不执行的
ActionManager& am = ActionManager::GetInstance();
am.QueueEventTrigger("early-init");//QueueEventTrigger用于触发Action,这里触发 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");
//QueueBuiltinAction用于添加Action,第一个参数是Action要执行的Command,第二个是Trigger
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
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(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
...
}
QueueEventTrigger
定义在platform/system/core/init/action.cpp
它并没有去触发trigger,而是构造了一个EventTrigger对象,放到队列中存起来
QueueBuiltinAction
定义在platform/system/core/init/action.cpp
这个函数有两个参数,第一个参数是一个函数指针,第二参数是字符串。首先是创建一个Action对象,将第二参数作为Action触发条件,将第一个参数作为Action触发后的执行命令,并且又把第二个参数作为命令的参数,最后是将Action加入触发队列并加入Action列表。
1.5 触发及监听
之前的所有工作都是往各种数组、队列里面存入信息,并没有真正去触发,而接下来的工作就是真正去触发这些事件,以及用epoll不断监听新的事件。
int main(int argc, char** argv) {
...
while (true) {
// By default, sleep until something happens.
int epoll_timeout_ms = -1; //epoll超时时间,相当于阻塞时间
if (do_shutdown && !shutting_down) {
do_shutdown = false;
if (HandlePowerctlMessage(shutdown_command)) {
shutting_down = true;
}
}
/*
* 1.waiting_for_prop和IsWaitingForExec都是判断一个Timer为不为空,相当于一个标志位
* 2.waiting_for_prop负责属性设置,IsWaitingForExe负责service运行
* 3.当有属性设置或Service开始运行时,这两个值就不为空,直到执行完毕才置为空
* 4.其实这两个判断条件主要作用就是保证属性设置和service启动的完整性,也可以说是为了同步
*/
if (!(waiting_for_prop || Service::is_exec_service_running())) {
am.ExecuteOneCommand(); //执行一个command
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
if (!shutting_down) {
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_timeout_ms设置为0
}
epoll_event ev;
/*
* 1.epoll_wait与上一篇中讲的epoll_create1、epoll_ctl是一起使用的
* 2.epoll_create1用于创建epoll的文件描述符,epoll_ctl、epoll_wait都把它创建的fd作为第一个参数传入
* 3.epoll_ctl用于操作epoll,EPOLL_CTL_ADD:注册新的fd到epfd中,EPOLL_CTL_MOD:修改已经注册的fd的监听事件,EPOLL_CTL_DEL:从epfd中删除一个fd;
* 4.epoll_wait用于等待事件的产生,epoll_ctl调用EPOLL_CTL_ADD时会传入需要监听什么类型的事件,
* 比如EPOLLIN表示监听fd可读,当该fd有可读的数据时,调用epoll_wait经过epoll_timeout_ms时间就会把该事件的信息返回给&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)();
}
}
...
}
ExecuteOneCommand
定义在platform/system/core/init/action.cpp
从名字可以看出,它只执行一个command,只执行一个。在函数一开始就从trigger_queue_队列中取出一个trigger,然后遍历所有action,找出满足trigger条件的action加入待执行列表current_executing_actions_中,接着从这个列表中取出一个action,执行它的第一个命令,并将命令所在下标自加1。由于ExecuteOneCommand外部是一个无限循环,因此按照上面的逻辑一遍遍执行,将按照trigger表的顺序,依次执行满足trigger条件的action,然后依次执行action中的命令.
RestartProcesses
定义在platform/system/core/init/init.cpp
restart_processes调用的其实是ForEachServiceWithFlags函数,这个函数主要是遍历services_数组,比较它们的flags是否是SVC_RESTARTING,也就是当前service是否是等待重启的,如果是就执行它的RestartIfNeeded函数。
RestartIfNeeded
定义在platform/system/core/init/service.cpp
这个函数将主要工作交给了Start,也就是具体的启动service,但是交给它之前做了一些判断,也就是5秒内只能启动一个服务,如果有多个服务,那么后续的服务将进入等待。
总结
Init进程根据语法一步步去解析.rc,将这些配置转换成一个个数组、队列,然后开启无限循环去处理这些数组、队列中的command和service,并且通过epoll监听子进程结束和属性设置。
参考文献
[1] 邓平凡.深入理解Android-卷Ⅰ.北京:机械工业出版社.2011-9
[2] Android 8.0 : 系统启动流程之init进程
[3] Android P (9.0) 之Init进程源码分析