Android 系统启动过程(一) —— 用户空间的鼻祖 init 进程
1. Android 系统启动过程
1.1 启动电源以及系统启动
当电源按下时引导芯片代码从预定义的地方(固化在ROM) 开始执行,这些代码的位置是预先定义好的,存储了系统启动初期需要执行的代码,这部分代码负责一些基本硬件的初始化和引导程序的加载工作。这意味着,当系统电源打开后,CPU 会自动跳转到这个预先定义的地址开始执行第一条指令。
1.2 引导程序 BootLoader
引导芯片中的代码会负责寻找、加载引导程序(BootLoader)到系统的随机访问存储器(RAM)中。引导程序 BootLoader 是在 Android 操作系统开始运行前的一个小程序,它的主要作用是把 OS 拉起来并运行,在这之前,它还可能提供一系列的功能,如系统配置、故障恢复选项等。最终将控制权交给操作系统,完成系统的启动过程。
1.3 Linux 内核启动
当内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。在内核完成系统设置后启动 init 进程。
1.4 init 进程启动
挂载文件相关系统、加载内核模块。初始化 SeLinux。启动属性服务、设置子进程信号处理函数、解析 rc 文件等。也用来启动 Zygote 进程。
1.5 zygote 进程启动
创建虚拟机并为虚拟机注册 JNI 方法,创建服务器端 Socket, 启动 SystemServer 进程。
1.6 SystemServer 进程启动
启动 Binder 线程池和 SystemServiceManager,并且启动各种系统服务。
1.7 Launcher 启动
被 SystemServer 进程启动的 AMS 会启动 Launcher,Launcher 启动后会将已安装应用 的快捷图标显示到界面上。
注:此处对 Android 系统启动过程做一个简单的概述,接下来会进行详解讲解和总结。
2. init 进程入口
本文代码基于 android-14.0.0_r9
。
swapper 进程(idle 进程),进程号为0,是 Android 系统中所有进程的鼻祖,是 Android 所有进程中唯一一个没有使用 fork/clone 方法被创建的进程,它创建了 init 进程和 kthreadd 进程。
init 进程是用户空间的第一个进程,进程号为1,是 Android 用户空间所有进程的鼻祖。kthreadd 进程是内核空间的第一个进程,进程号为2。见名知意,init 进程主要负责初始化工作。
通过 adb 命令查看 init 进程相关信息:
注:带有 APIs 标识版本的模拟器上可以使用 adb。
模拟器内核源码下载:
git clone https://aosp.tuna.tsinghua.edu.cn/kernel/goldfish.git
cd goldfish
git branch -a
git checkout *****
goldfish 是模拟器内核代码(内核代码 origin/android-goldfish-5.4-dev):
// goldfish/init/main.c
noinline void __ref rest_init(void)
{
...
// 创建一个进程,创建完毕会返回值为1的 pid,并且会执行 kernel_init 函数
pid = kernel_thread(kernel_init, NULL, CLONE_FS);
...
// 创建一个进程,pid为2,并且会执行 kthreadd 函数
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
...
}
kernel_thread 是内核创建进程的方法。接下来查看一下 kernel_init 函数:
static int __ref kernel_init(void *unused)
{
...
// 依次执行下面目录中的 init 程序,成功就退出
if (!try_to_run_init_process("/sbin/init") ||
!try_to_run_init_process("/etc/init") ||
!try_to_run_init_process("/bin/init") ||
!try_to_run_init_process("/bin/sh"))
return 0;
panic("No working init found. Try passing init= option to kernel. "
"See Linux Documentation/admin-guide/init.rst for guidance.");
}
调用 try_to_run_init_process 执行 init 程序,init 程序是一个 so 库:
static int try_to_run_init_process(const char *init_filename)
{
int ret;
ret = run_init_process(init_filename);
if (ret && ret != -ENOENT) {
pr_err("Starting init: %s exists but couldn't execute it (error %d)\n",
init_filename, ret);
}
return ret;
}
static int run_init_process(const char *init_filename)
{
argv_init[0] = init_filename;
pr_info("Run %s as init process\n", init_filename);
return do_execve(getname_kernel(init_filename),
(const char __user *const __user *)argv_init,
(const char __user *const __user *)envp_init);
}
通过 do_execve 函数会在用户空间执行 init.so 的 main 函数,启动了 init 进程的初始化过程。do_execve 是 execve 系统调用的实际执行函数,execve 系统调用用于在用户空间中执行一个新的程序并替换当前进程的代码和数据。
在内核完成系统设置后启动 init 进程,init 进程的入口 system/core/init/main.cpp
:
// system/core/init/main.cpp
int main(int argc, char** argv) {
...
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map);
}
if (!strcmp(argv[1], "selinux_setup")) {
// 2. 初始化 SeLinux 等
return SetupSelinux(argv);
}
if (!strcmp(argv[1], "second_stage")) {
// 3. 启动属性服务、设置子进程信号处理函数、解析 rc 文件等
return SecondStageMain(argc, argv);
}
}
// 1. 挂载相关文件系统等
return FirstStageMain(argc, argv);
}
该入口函数将各阶段工作分离开,简洁明了,该函数将会依次执行:
- FirstStageMain:挂载相关文件系统、加载内核模块等。
- SetupSelinux:初始化 SeLinux 等。
- SecondStageMain:启动属性服务、设置子进程信号处理函数、解析 rc 文件等。
2.1 FirstStageMain
FirstStageMain 通过 mount 挂载对应的文件系统,mkdir 创建对应的文件目录,并配置相应的访问权限。
创建和挂载启动所需的文件目录,如 tmpfs、devpts 等。这些都是系统运行时目录。
// system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
boot_clock::time_point start_time = boot_clock::now();
std::vector<std::pair<std::string, int>> errors;
#define CHECKCALL(x) \
if ((x) != 0) errors.emplace_back(#x " failed", errno);
// Clear the umask.
umask(0);
// 创建挂载相关文件系统
CHECKCALL(clearenv());
CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
CHECKCALL(mkdir("/dev/pts", 0755));
CHECKCALL(mkdir("/dev/socket", 0755));
CHECKCALL(mkdir("/dev/dm-user", 0755));
CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR
...
SetStdioToDevNull(argv);
InitKernelLogging(argv);
...
LOG(INFO) << "init first stage started!";
...
int module_count = 0;
// 加载内核模块
if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
module_count)) {
if (want_console != FirstStageConsoleParam::DISABLED) {
LOG(ERROR) << "Failed to load kernel modules, starting console";
} else {
LOG(FATAL) << "Failed to load kernel modules";
}
}
...
// 通过 execv 方法传递对应的 path 与下一阶段的参数 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);
// 再次执行 main,此处传入了 selinux_setup 参数
execv(path, const_cast<char**>(args));
PLOG(FATAL) << "execv(\"" << path << "\") failed";
return 1;
}
FirstStageMain 函数最后通过 execv 方法传递对应的 path 与下一阶段的参数 selinux_setup,继续执行 SetupSelinux 函数。
2.2 SetupSelinux
这阶段主要是初始化 SeLinux。SELinux 是安全加强型 Linux,能够很好的对全部进程强制执行访问控制,从而让 Android 更好的保护和限制系统服务、控制对应用数据和系统日志的访问,提高系统安全性:
// system/core/init/selinux.cpp
int SetupSelinux(char** argv) {
SetStdioToDevNull(argv);
InitKernelLogging(argv);
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
boot_clock::time_point start_time = boot_clock::now();
SelinuxSetupKernelLogging();
if (IsMicrodroid()) {
LoadSelinuxPolicyMicrodroid();
} else {
LoadSelinuxPolicyAndroid();
}
SelinuxSetEnforcement();
if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {
const int flags = SELINUX_ANDROID_RESTORECON_RECURSE;
if (selinux_android_restorecon("/microdroid_resources", flags) == -1) {
PLOG(FATAL) << "restorecon of /microdroid_resources failed";
}
}
if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
}
setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
execv(path, const_cast<char**>(args));
PLOG(FATAL) << "execv(\"" << path << "\") failed";
return 1;
}
接下来调用 execv 进入到最后阶段 SecondStageMain。
2.3 SecondStageMain
SecondStageMain 是 init 进程启动过程中非常重要的一个阶段:
// system/core/init/init.cpp
int SecondStageMain(int argc, char** argv) {
...
// 对属性服务进行初始化
PropertyInit();
...
// 设置子进程信号处理函数。主要用于防止init进程的子进程成为僵尸进程。
InstallSignalFdHandler(&epoll);
// 启动属性服务
StartPropertyService(&property_fd);
...
// 解析 init 相关的 .rc 文件
LoadBootScripts(am, sm);
...
}
init 的 SecondStageMain 函数中我们主要关注这几件事:
- PropertyInit、StartPropertyService:对属性服务进行初始化并启动。
- InstallSignalFdHandler:设置子进程信号处理函数。
- LoadBootScripts:解析 rc 脚本文件。
接下来我们对这几点进行详细讲解。
3. SecondStageMain 过程
3.1 属性服务
属性服务有着非常重要的作用,属性服务解决了子进程之间或者子进程与 init 进程之间的通信问题。
属性服务用于在系统中存储和共享全局变量。这些属性可以被系统中的不同进程访问和修改,从而实现进程间的通信。属性服务通常用于配置和控制系统的各种行为,例如网络状态、调试模式、系统语言等。
属性服务提供了系统级的存取 key-value 键值对的功能。在应用进程之间,一个进程设置了 key-value 数据,其他的进程是可以获取到的,属性服务就提供了系统级的这种功能。
属性服务的设置,可以通过使用 SystemProperties api 或 adb 等方式设置属性服务:
// 1. java 层,SystemProperties api
// 存储 key-value
SystemProperties.set(key,value);
// 根据 key 获取 value
SystemProperties.get(key)
————————————————————————————————————————————————————————————————————————————————————————————
// 2. 通过 adb 方式
adb root
adb shell
// 设置 xxx.xx 为value
setprop xxx.xx value
// 获取 xxx.xx 的值
getprop xxx.xx
为什么要在此阶段初始化并开启属性服务:
- init 进程在整个用户空间的所有进程中它的生命周期是最长的,所以属性服务在 init 进程启动的阶段启动,并常驻于此,可以为别的进程提供更稳定的服务。
- Action 中 property 类型的 trigger 需要使用到属性服务,没有属性服务 init 进程根本不能正常的不断运行。(下文即将讲解)
3.2 设置子进程信号处理函数
3.2.1 僵尸进程
在 Linux/UNIX 中,在子进程终止后,如果父进程并不知道子进程已经终止了,虽然此时子进程已经退出了,但是子进程的进程描述符(Process Descriptor)和最基本的信息还会保留被在系统中,这个子进程就是僵尸进程。如果系统进程表被僵尸进程耗尽,就无法创建新的进程,僵尸进程会消耗系统资源。
3.2.2 监听子进程终止
所以为了防止僵尸进程的出现,需要监听子进程终止。系统会在子进程终止时发出 SIGCHLD 信号。
init 进程如何监听子进程终止:
- 注册 SIGCHLD 信号:init 进程使用 sigaction 函数来注册 SIGCHLD 信号。SIGCHLD 是一个特殊的信号,通过注册这个信号,init 进程就能够知道它的子进程状态发生了变化。
- 生成信号文件描述符:使用 signalfd 函数为 SIGCHLD 信号生成一个文件描述符 fd。
- 使用epoll监听文件描述符: 使用 epoll 来见监听上一步生成的 fd 是否有可读数据。
- 获取终止的子进程信息: 如监听到 fd 上有可读数据,则证明子进程的状态发生了变化,还需要使用 waitpid 函数来获取是哪个子进程终止了。
// system/core/init/init.cpp
static void InstallSignalFdHandler(Epoll* epoll) {
...
// 注册 SIGCHLD 信号
sigaction(SIGCHLD, &act, nullptr);
// Register a handler to unblock signals in the child processes.
const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals);
if (result != 0) {
LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
}
Result<void> cs_result = RegisterSignalFd(epoll, SIGCHLD, Service::GetSigchldFd());
if (!cs_result.ok()) {
PLOG(FATAL) << cs_result.error();
}
if (!IsRebootCapable()) {
Result<int> cs_result = CreateAndRegisterSignalFd(epoll, SIGTERM);
if (!cs_result.ok()) {
PLOG(FATAL) << cs_result.error();
}
sigterm_fd = cs_result.value();
}
}
static Result<int> CreateAndRegisterSignalFd(Epoll* epoll, int signal) {
sigset_t mask;
// 初始化并清空一个信号集,使其不包含任何信号
sigemptyset(&mask);
// 把 SIGCHLD 信号加入 mask 信号集中
sigaddset(&mask, signal);
if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
return ErrnoError() << "failed to block signal " << signal;
}
// 调用 signalfd 函数为 mask 生成一个 fd
unique_fd signal_fd(signalfd(-1, &mask, SFD_CLOEXEC));
if (signal_fd.get() < 0) {
return ErrnoError() << "failed to create signalfd for signal " << signal;
}
OR_RETURN(RegisterSignalFd(epoll, signal, signal_fd.get()));
return signal_fd.release();
}
使用 epoll 来监听 signal_fd 上的数据:
static Result<void> RegisterSignalFd(Epoll* epoll, int signal, int fd) {
// 使用 epoll 来监听 signal_fd 上的数据
return epoll->RegisterHandler(
fd, [signal]() { HandleSignalFd(signal); }, EPOLLIN | EPOLLPRI);
}
如果 fd 上有数据就会调用 HandleSignalFd:
static void HandleSignalFd() {
// 读取到 siginfo 信息
signalfd_siginfo siginfo;
const int signal_fd = signal == SIGCHLD ? Service::GetSigchldFd() : sigterm_fd;
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
if (bytes_read != sizeof(siginfo)) {
PLOG(ERROR) << "Failed to read siginfo from signal_fd";
return;
}
// 判断当前的 ssi_signo
switch (siginfo.ssi_signo) {
case SIGCHLD:
ReapAnyOutstandingChildren();
break;
case SIGTERM:
HandleSigtermSignal(siginfo);
break;
default:
PLOG(ERROR) << "signal_fd: received unexpected signal " << siginfo.ssi_signo;
break;
}
}
ssi_signo 为 SIGCHLD 时,调用 ReapAnyOutstandingChildren 函数:
// system/core/init/sigchld_handler.cpp
std::set<pid_t> ReapAnyOutstandingChildren() {
std::set<pid_t> reaped_pids;
for (;;) {
const pid_t pid = ReapOneProcess();
if (pid <= 0) {
return reaped_pids;
}
reaped_pids.emplace(pid);
}
}
static pid_t ReapOneProcess() {
siginfo_t siginfo = {};
// 调用 waitpid 方法来获取死掉的子进程的信息
if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
PLOG(ERROR) << "waitid failed";
return 0;
}
const pid_t pid = siginfo.si_pid;
if (pid == 0) {
DCHECK_EQ(siginfo.si_signo, 0);
return 0;
}
...
}
监听到子进程终止时会通过 waitpid 方法获取到子进程的 pid。根据 pid 可以找到子进程对应的 Service,子进程的配置信息都会放在 Service 类中,Service 持有了创建子进程的时候持有的各种 socket 等资源,这些资源会被清除掉,并且会通知 kernel 杀掉子进程,进而在 kernel 层清除掉子进程占据的各种资源。
3.3 解析 rc 文件
3.3.1 Android Init Language
作为用户空间的鼻祖,init 进程会创建非常多的进程。如果 init 进程主动地一个一个去创建每一个进程,过程将会变得极为复杂,因为每个进程都会在不同的时期、不同的条件下进行创建。创建几个进程还好,数量多了,会变得很乱,init 进程根本忙不过来。
为了更方便管理子进程的创建,这些创建规则和条件会被预先配置好,并在 init 进程启动过程中通过读取和解析这些配置来决定创建哪些子进程。
如何配置信息呢?这些配置是通过脚本语言 Android Init Language(Android 初始化语言) 进行编写,此类文件名称以 ".rc" 结尾。通过定义 Android Init Language 脚本,系统可以清晰地描述哪些服务应该在何时启动,以及它们之间的依赖关系。这种方式使得系统配置更加模块化。
注:
system/core/init
路径下的 README.md 文件对 Android Init Language 进行了详细的描述。
Android 初始化语言包含五种主要的语句类型:Actions, Commands, Services, Options, 和 Imports。
rc 文件是以 section 为单位的,一个 section 可以包含多行,section 可以分为两大类,一类是 Actions,另一类是 Services。
3.3.1.1 Actions
Actions 由一系列命令组成, 主要配置子进程在创建之前需要执行一些提前操作或者提前执行的命令,比如有的子进程在创建之前需要提前创建一些目录等操作。
Actions 格式:
on <trigger> [&& <trigger>]*
<command>
<command>
<command>
第一行使用 on 关键字配置了 Action 触发条件(trigger),决定了什么时候开始执行这一系列命令。当一个事件与一个 Action 的触发器匹配时,该 Action 被添加到一个待执行队列的尾部。
triggers 是可用于匹配某些类型的事件并用于导致操作发生的字符串。可以分为 event triggers 和 property triggers。
Commands 就是将要执行的命令。
举个例子:
on boot
setprop a 1
setprop b 2
on boot && property:true=true
setprop c 1
setprop d 2
on boot
setprop e 1
setprop f 2
其中 boot 为 event triggers,property:true=true 为 property triggers。一个 Action 可以有多个属性触发器,但只能有一个事件触发器。setprop 是一个 Command,是一个设置属性值的命令。
当 boot 发生时,并且 property:true=true 时,这些命令将按照以下顺序执行:
setprop a 1
setprop b 2
setprop c 1
setprop d 2
setprop e 1
setprop f 2
当 boot 发生时,并且 property:true 不为 true 时,这些命令将按照以下顺序执行:
setprop a 1
setprop b 2
setprop e 1
setprop f 2
3.3.1.2 Services
使用 service 关键字配置子进程基础信息,比如子进程的名字、可执行文件路径等,init 进程就可以立马明白是哪个子进程被创建。
Services 格式:
# name:子进程的名字
# pathname:可执行文件路径
# argument:可执行文件的 main 方法被执行的时候,参数会传递到 main 方法
# option:其他的一些配置信息,比如子进程是否可重启等
service <name> <pathname> [ <argument> ]*
<option>
<option>
...
Options 是 Services 的修饰项,决定运行 service 运行的时间和方式。
每个 Service 对应一个 rc 文件。如 Zygote 进程启动脚本在 init.zygotexxx.rc 中定义。
3.3.1.3 Imports
通过 import 关键字配置信息脚本文件引用。如果 path 是一个目录,该目录中的每个文件都被解析为一个配置文件。它不是递归的,嵌套的目录将不会被解析。
Imports 也是一种 section,不是 action 中的一种命令。
import xxx/xxx/xx.rc
3.3.2 init 进程中相关的类
3.3.2.1 Service
Service 类中存储了通过脚本文件配置的子进程的基础信息,并且还存储了子进程在创建成功以后的信息,比如 pid,启动时间等,如下代码:
// system/core/init/service.h
class Service {
...
// servicename
std::string name_;
// classname
std::set<std::string> classnames_;
unsigned flags_;
// 子进程的pid
pid_t pid_;
// 子进程开始运行的时间
android::base::boot_clock::time_point time_started_;
// 子进程崩溃的时间
android::base::boot_clock::time_point time_crashed_;
// 子进程崩溃的次数
int crash_count_;
...
}
3.3.2.2 ServiceList
ServiceList 用于存储各种子进程的配置信息 Service 的实例。
// system/core/init/service_list.h
class ServiceList {
// 存储 Service 实例
private:
std::vector<std::unique_ptr<Service>> services_;
bool post_data_ = false;
std::vector<std::string> delayed_service_names_;
}
3.3.2.3 Action
Action 类包含了脚本配置信息中的触发条件和触发条件下的各种命令。
// system/core/init/action.h
class Action {
private:
// property 类型的触发条件
std::map<std::string, std::string> property_triggers_;
// event 类型的触发条件
std::string event_trigger_;
// 包含的所有命令
std::vector<Command> commands_;
// 为true 则代表子进程死掉的话就不会被重新创建;否则重新创建
bool oneshot_;
}
3.3.2.4 ActionManager
与 ServiceList 类似,它存储了所有的 Action 的实例。
// system/core/init/action_manager.h
class ActionManager {
private:
// 所有的 Action 实例
std::vector<std::unique_ptr<Action>> actions_;
}
3.3.3 LoadBootScripts 解析 rc 文件
init 进程启动过程中会调用 system/core/init/init.cpp
的 LoadBootScripts 方法去加载所有的配置信息。LoadBootScripts 方法的代码:
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()) {
parser.ParseConfig("/system/etc/init/hw/init.rc");
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
// late_import is available only in Q and earlier release. As we don't
// have system_ext in those versions, skip late_import for system_ext.
parser.ParseConfig("/system_ext/etc/init");
if (!parser.ParseConfig("/vendor/etc/init")) {
late_import_paths.emplace_back("/vendor/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
} else {
parser.ParseConfig(bootscript);
}
}
通过 LoadBootScripts 将 rc 文件分别解析到了 Service 和 Action 实例中,所有的 Service 实例被保存在了 ServiceList 实例中,所有的 Action 实例保存在了 ActionManager 实例中。
注:由于篇幅较长,并且本文着重于分析启动流程,此处暂省略 rc 文件解析的具体步骤。
4. init 循环工作模式
SecondStageMain 中完成属性服务启动、注册子信号、解析 rc 文件等操作后,就会进入到 init 进程的循环工作模式:
int SecondStageMain(int argc, char** argv) {
...
while (true) {
const boot_clock::time_point far_future = boot_clock::time_point::max();
boot_clock::time_point next_action_time = far_future;
// 是否执行了关机操作
auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command) {
LOG(INFO) << "Got shutdown_command '" << *shutdown_command
<< "' Calling HandlePowerctlMessage()";
HandlePowerctlMessage(*shutdown_command);
}
// 是否有事件需要处理
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
// If there's more work to do, wake up again immediately.
if (am.HasMoreCommands()) {
next_action_time = boot_clock::now();
}
}
...
}
return 0;
}
init 进程会不断的循环执行,我们这里主要关注一下下面的操作:
- 关机或重启:直接执行关机或重启操作,后面的操作不会在执行,关机或重启的优先级最高的。
- 不是关机或重启:调用 ActionManage 的 ExecuteOneCommand 方法。
Action 的执行是怎么触发的?首先将各种各样的触发器放入 ActionManager 中:
- event triggers:直接调用 ActionManager 的 QueueEventTrigger 函数。
- property triggers:设置属性服务 key-value 值,属性信息最终同步到 ActionManager。
由于 Action 的执行具有依赖关系,所以在进入 init 进程的循环模式之前,依次触发 early-init、init 以及 late-init:
int SecondStageMain(int argc, char** argv) {
...
LoadBootScripts(am, sm);
...
am.QueueEventTrigger("early-init");
...
am.QueueEventTrigger("init");
...
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
...
while (true) {
...
}
return 0;
}
ActionManager 会不断接收到不同类型的触发器,此时触发器对应的 Action 并没有执行。需要 init 进程循环调用 ExecuteOneCommand 函数触发对应的 Action 执行。
4.1 关机或重启
在低电量情况下,PowerManagerService 服务会调用 SystemProperties 的 set 方法来为 sys.powerctl 的 key 设置 value。属性服务会监听到 sys.powerctl 的 property 属性信息,把这个属性信息保存下来,在每次循环的时候都会检查是否有关机或重启的属性信息,有的话就开始执行关机或重启操作。
关机或重启的核心关键原理就是进程之间的通信。
4.2 ExecuteOneCommand
ExecuteOneCommand 方法中将按照 event_queue_ 的顺序,依次取出 action 链表中与 trigger 匹配的 action。 每次均执行一个 action 中的一个 command 对应函数。 当一个 action 所有的 command 均执行完毕后,再执行下一个 action。 当一个 trigger 对应的 action 均执行完毕后,再执行下一个 trigger 对应 action:
void ActionManager::ExecuteOneCommand() {
{
auto lock = std::lock_guard{event_queue_lock_};
// Loop through the event queue until we have an action to execute
while (current_executing_actions_.empty() && !event_queue_.empty()) {
// 遍历 actions_
for (const auto& action : actions_) {
// 判断 action 是否需要执行
if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
event_queue_.front())) {
//如果满足这证明该action需要执行,把action压入current_executing_actions_当前执行队列中。
current_executing_actions_.emplace(action.get());
}
}
event_queue_.pop();
}
}
if (current_executing_actions_.empty()) {
return;
}
// 从当前需要执行的action队列中取出第一个要执行的action
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() << ")";
}
// 开始执行 action
action->ExecuteOneCommand(current_command_);
// 如果这是当前需要执行的action的最后一个命令,则从current_executing_actions_队列中移除该action
// 如果这个action只执行依次,则从actions_向量中移除它
++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),
actions_.end());
}
}
}
转载自:https://juejin.cn/post/7383914669307183144