Android Framework 笔记02 - init 进程启动过程

init 进程是 Android 系统中用户空间的第一个进程,进程号为 1,是 Android 系统启动流程中一个关键的步骤,作为第一个进程,它被赋予了很多极其重要的工作职责,比如创建 Zygote (孵化器) 和属性服务等。init 进程是由多个源文件共同组成的,这些文件位于源码目录 system/core/init 中。

引入 init 进程

为了讲解 init 进程,首先了解一下 Android 系统启动流程的前几步,以引入 init 进程。

1)启动电源以及系统启动

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

2)引导程序 BootLoader

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

3)Linux 内核启动

当内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。在内核完成系统设置后,它首先在系统文件中寻找 init.rc 文件,并启动 init 进程。

4)init 进程启动

init 进程做的工作比较多,主要用来初始化和属性服务,也用来启动 Zygote 进程。

从上面的步骤可以看出,当我们按下电源时,系统启动会加载引导程序,引导程序又启动 Linux 内核,在 Linux 内核完成后,第一件事就是要启动 init 进程。

init 进程的入口函数

在 Linux 内核加载完成后,它首先在系统文件中寻找 init.rc 文件,并启动 init 进程,init 进程的入口函数 main,代码如下:

int main(int argc, char** argv) {

// ...

bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);

if (is_first_stage) {
// ...
}

// ...

// 1. 对属性服务进行初始化
property_init();

// ...

// 创建 epoll 句柄
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
PLOG(FATAL) << "epoll_create1 failed";
}

// 2. 用于设置子进程信号处理函数,如果子进程 (Zygote 进程) 异常退出,init 进程
// 会调用该函数中设定的信号处理函数进行处理
sigchld_handler_init();

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

// 3. 启动属性服务
start_property_service();
set_usb_controller();

// ...

// 4. 解析加载 init.rc 配置文件
LoadBootScripts(am, sm);

// ...
while (true) {
// By default, sleep until something happens.
int epoll_timeout_ms = -1;

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

if (!(waiting_for_prop || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
if (!shutting_down) {

// 5. 重启死去的进程
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_event 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)();
}
}

return 0;
}

// 解析加载 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()) {
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);
}
}

1) 注释 1 和 3 都是关于属性服务

在注释 1 处 property_init 函数来对属性进行厨师阿虎,并在注释 3 处调用 start_property_service 启动属性服务

2)注释 2 处设置子进程信号处理函数

注释 2 处调用 sigchld_handler_init 用于设置子进程信号处理函数,主要用于防止 init 进程的子进程成为僵尸进程,为了防止僵尸进程的出现系统会在子进程暂停和终止的时候发出 SIGCHLD 信号,而 sigchld_handler_init 函数就是用来接收这个信号的(其内部只处理进程终止的 SIGCHLD 信号)。

在 UNIX/Linux 中,父进程使用 fork 创建子进程,在子进程终止之后,如果父进程并不知道子进程已经终止了,这时子进程虽然已经推出了,但是在系统进程表中还为它保留了一定的信息(比如进程号、退出状态、运行时间等),这个子进程就被称作僵尸进程。系统进程表示一项有限资源,如果被僵尸进程耗尽的话,系统就可能无法创建新的进程了。

3)注释 5 负责重启 init 子进程

Zygote 也是 init 进程的子进程之一,如果出现子进程终止的情况就会触发 sigchld_handler_init 并触发清理 Zygote 进程的信息,然后 Zygote 就会在注释 5 处被重启。

4)注释 4 用来解析 init.rc 文件

init.rc 是一个非常重要的配置文件,它是由 Android 初始化语言(Android Init Language)编写的脚本,这种语言主要包含 5 种类型语句:Action、Command、Service、Option 和 Import。

Zygote 的配置与启动

1)Zygote 配置

从 Android 8.0 开始对 init.rc 文件进行了拆分,每个服务对应一个 rc 文件,下面是 Zygote 启动脚本,在 init.zygote64.rc 中。代码如下:

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks

Service 命令用于通知 init 进程 Zygote 进程的名为 zygote,这个进程执行程序的路径为 system/bin/app_process64,后面的代码是要传给 app_process64 的参数。class main 指的是 Zygote 的 classname 为 main。

Service 命令会由 init main 函数的代码中解析为 Service 对象,再添加到 ServiceManager 中 vector 类型的 Service 链表中,执行 Service 命令解析的主要涉及文件是是 system/core/init/service.cpp 执行的时候

2)Zygote 进程的真正启动

init.rc 是一个配置文件,在 Service 命令定义 Zygote 后还有如下配置代码:

on nonencrypted
class_start main
class_start late_start

其中 clas_start 对应的函数执行是 system/core/init/builtins.cpp 中的 do_class_start,这里就是启动 classname 为 main 的 Service,其中 zygote 就是在此处启动的。如下:

static Result<Success> do_class_start(const BuiltinArguments& args) {
// Starting a class does not start services which are explicitly disabled.
// They must be started individually.
for (const auto& service : ServiceList::GetInstance()) {
if (service->classnames().count(args[1])) {
if (auto result = service->StartIfNotDisabled(); !result) {
LOG(ERROR) << "Could not start service '" << service->name()
<< "' as part of class '" << args[1] << "': " << result.error();
}
}
}
return Success();
}

其中 Zygote 执行程序的路径是 /system/bin/app_process64 对应的文件为 framework/base/cmds/app_process/app_main.cpp,部分代码如下:

int main(int argc, char* const argv[])
{
// ...

if (zygote) {
// 1. 启动 Zygote
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
}
}
  • 如上注释 1 处调用 runtime 的 start 函数启动 Zygote,至此 Zygote 就启动了。

Android 系统的属性服务

Android 系统提供了属性服务,其实是类似于 Windows 系统中的注册表管理器,用于记录用户的一些使用信息,从而在系统或软件重启后,扔能够获取存储的记录,进行相应的初始化工作。

init 进程启动时会启动属性服务,并为其分配内存,用来存储这些属性,如果需要属性直接读取就可以了,上面 init.cpp 的提到的 property_init, start_property_service 就是用于初始化属性配置和启动属性服务的。

void start_property_service() {
selinux_callback cb;
cb.func_audit = SelinuxAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);

property_set("ro.property_service.version", "2");

// 1
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, nullptr);
if (property_set_fd == -1) {
PLOG(FATAL) << "start_property_service socket creation failed";
}

// 2
listen(property_set_fd, 8);

// 3
register_epoll_handler(property_set_fd, handle_property_set_fd);
}
  • 属性服务通过创建非阻塞的 Socket
  • 属性服务通过 listen 函数对 property_set_fd 进行设置最多同时可以为 8 个试图设置属性服务的用户提供服务。
  • 最终将 property_set_fd 放入 epoll 中,用 epoll 监听收到新数据的时候回调 handle_property_set_fd 函数

ps: epoll 为 Linux 内核为处理大批量文件描述符而做了改进的 poll,是 Linux 下多路复用 I/O 接口 select/poll 的增强版本,epoll 内部使用数据结构是红黑树,而 select 使用数组,当存在大量文件描述符时,epoll 查找效率会比 select 速度快。

属性服务中分为控制属性和普通属性,控制属性用来执行一些命令,比如开机的动画就使用了这个属性。控制属性和普通属性的存储、更新逻辑不一致,控制属性的 key 名称以 ctl. 开头。

总结

init 进程启动做了很多工作,总的来说做了三件事

  • 创建和挂载启动所需的文件目录
  • 初始化和启动属性服务
  • 解析 init.rc 并执行启动 Zygote 进程