init 进程进入 main 函数后,主要完成以下四项工作。第一,在根文件系统中创建目录作为挂载点,然后挂载虚拟文件系统;第二, 解析启动脚本文件 init.rc;第三,根据启动脚本 init.rc 的解析结果开启 Android Framework 核心服务进程;第四,监听事件,重启服务。
1. 在根文件系统中创建目录,挂载虚拟文件系统
虚拟文件系统不会保存在外部存储设备中,开机时由内核产生。除了 tmpfs 外,这些文件系统用作内核空间和用户空间的访问接口。它们的基本信息如下表所示:
文件系统 | 挂载点 | 主要用途 |
---|---|---|
tmpfs | /dev | 用于保存临时文件,提高设备访问效率 |
devpts | /dev/pts | 支持虚拟终端设备 |
proc | /proc | 访问内核数据结构的接口 |
sysfs | /sys | 访问系统数据的接口,特点是one-value-per-file |
通过系统调用 mount 挂载虚拟文件系统,代码实现如下所示。
代码路径:system/core/init/init.c
int main(int argc, char **argv)
{
......
mkdir("/dev", 0755);
mkdir("/proc", 0755);
mkdir("/sys", 0755);
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
mount("proc", "/proc", "proc", 0, NULL);
mount("sysfs", "/sys", "sysfs", 0, NULL);
......
}
2. 解析启动脚本文件 init.rc
init.rc 文件在系统根目录下,包含了 Android Framework 的初始化内容,其内部会 import 其他的初始化文件,比如 init.environ.rc
文件,init.usb.rc
文件,init.{ro.hardware}.rc
文件,以及 init.${ro.zygote}.rc
文件。
init.rc 按照一种特定的语法描述,也就是 Android 初始化语言 (Android Init Language) 来描述。Android 初始化语言的帮助文档位于 system/core/init/readme.txt
。
Android 初始化语言有以下六个基本概念。
Action
Action 由关键字 on 来声明。Action 格式如下:
on <trigger>
<command>
<command>
...
<trigger> 是触发条件,同时也是 Action 的名字。Action 满足触发条件时就会执行 <command>
Service
Service 由关键字 service 来声明。系统服务进程比如 servicemanager, zygote, uevent, installd 等都是一个 Service。Service 格式如下:
service <name> <pathname> [ <argument> ]*
<option>
<option>
...
<name>表示 service 的名字,必须要指定
<pathname>表示 service 的执行路径,必须要指定
<argument>表示传给 service 的参数,可以为0个或多个
<option>表示 service 启动的配置参数
Section
Section 是 init.rc 的基本组成单元。每个Action 或者 Service 都是一个 Section。需要注意的是 Section 的名字不能重复,否则只有第一个 Section 定义有效。
Option
Option 来用修饰 Service,由它来指定何时以及如何启动 Service。比如,critical 表示一个 Service 是系统关键服务,退出后 init 进程会重启服务。如果一分钟内该服务退出四次,就会触发整个系统重启。
Trigger
Trigger 表示一个自定义的触发条件,用来触发 Action 中 Command 的执行。early-init, init, early-fs, fs, post-fs, post-fs-data, charger, early-boot , boot等都是一个 Trigger。
Command
Command 是最小功能单元,表示一个 Linux 命令或者一个方法调用。
解析 init.rc 的工作由函数init_parse_config_file 来完成。具体细节可以参考初始化文件(init.rc)解析 这篇文章。解析完成后,得到一个 service_list 链表和一个 action_list 链表。其中,service_list 链表用来保存 service 的解析结果,action_list 链表用来保存 action 的解析结果。如下图所示:
除了启动脚本文件 init.rc 可以添加 action 到 action_list 链表外,调用 queue_builtin_action 函数也可以添加 action 到 action_list 链表,同时该函数还会调用 action_add_queue_tail 将该 action 添加到 action_queue 链表末尾,以便后面执行 action 中的 command。
代码路径:system/core/init/init_parser.c
void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
{
struct action *act;
struct command *cmd;
act = calloc(1, sizeof(*act));
act->name = name;
list_init(&act->commands);
list_init(&act->qlist);
cmd = calloc(1, sizeof(*cmd));
cmd->func = func;
cmd->args[0] = name;
list_add_tail(&act->commands, &cmd->clist);
list_add_tail(&action_list, &act->alist);
action_add_queue_tail(act);
}
为了让 action 按照一定的顺序触发,使用 action_for_each_trigger 函数来根据 trigger 的内容(early-init -> init -> early-fs -> fs -> post-fs -> post-fs-data -> charger -> early-boot -> boot)对 action 在链表中的顺序进行调整。调整后的 action 由 action_queue 链表来保存。过程如下所示:
代码路径:system/core/init/init.c
int main(int argc, char **argv)
{
......
action_for_each_trigger("early-init", action_add_queue_tail);
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
queue_builtin_action(keychord_init_action, "keychord_init");
queue_builtin_action(console_init_action, "console_init");
/* execute all the boot actions to get us started */
action_for_each_trigger("init", action_add_queue_tail);
/* skip mounting filesystems in charger mode */
if (!is_charger) {
action_for_each_trigger("early-fs", action_add_queue_tail);
action_for_each_trigger("fs", action_add_queue_tail);
action_for_each_trigger("post-fs", action_add_queue_tail);
action_for_each_trigger("post-fs-data", action_add_queue_tail);
}
queue_builtin_action(property_service_init_action, "property_service_init");
queue_builtin_action(signal_init_action, "signal_init");
queue_builtin_action(check_startup_action, "check_startup");
if (is_charger) {
action_for_each_trigger("charger", action_add_queue_tail);
} else {
action_for_each_trigger("early-boot", action_add_queue_tail);
action_for_each_trigger("boot", action_add_queue_tail);
}
/* run all property triggers based on current state of the properties */
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
......
}
3. 根据启动脚本 init.rc 的解析结果开启核心服务进程
通过之前的初始化工作得到 action_queue 链表后,调用 execute_one_command 函数遍历 action_queue 链表,并执行链表中 action 的 command,实际上是调用 command 中 func 函数指针指向的处理函数。
代码路径:system/core/init/init.h
struct command
{
/* list of commands in an action */
struct listnode clist;
int (*func)(int nargs, char **args);
int nargs;
char *args[1];
};
下面以 boot action (boot 既是 action 的触发条件,又可以作为 action 的名称) 为例,梳理 zygote 服务进程的启动流程,其他核心服务进程的启动过程也类似。
在这些启动的核心服务进程中,其中两个最重要的核心服务进程是 zygote 进程和 servicemanager 进程。
zygote 进程是 Android 启动后的第一个虚拟机进程。它主要负责启动 system_server 进程,以及所有的应用程序进程,所以也称为孵化器进程。zygote 进程会启动子进程 system_server,system_server 进程在 nativeInit 函数中启动 Native 系统服务比如 SensorService,在 ServerThread 的 initAndLoop 函数中启动 Java 系统服务比如 ActivityManagerService, PackageManagerService, ContentService 等,并将系统服务注册进 ServiceManager。
servicemanager 进程是 Binder 进程间通信机制的核心之一。它负责管理系统中所有的 Service 组件,提供 Service 组件的注册服务,并且向 Client 组件提供获取 Service 代理对象的服务。
以下是 boot action 在 init.rc 文件中的内容:
on boot
......
class_start core
class_start main
在调用 init_parse_config_file 函数完成解析后,action_queue 链表中名字为 boot 的 action 包含两个 command,这两个 command 的 func 都赋值为 do_class_start,只是一个 command 的参数为 core;另一个 command 的参数为 main。当 execute_one_command 遍历到 boot 这个 action 时,依次执行这两个 command,即调用 do_class_start 函数。
代码路径:system/core/init/builtins.c
int do_class_start(int nargs, char **args)
{
/* Starting a class does not start services * which are explicitly disabled. They must * be started individually. */
service_for_each_class(args[1], service_start_if_not_disabled);
return 0;
}
do_class_start 内部调用service_for_each_class 函数遍历前面解析得到的 service_list,如果发现某个 service 的 classname 与参数 args[1] 相同,则调用 service_start_if_not_disabled 函数启动该 service。
代码路径:system/core/init/builtins.c
static void service_start_if_not_disabled(struct service *svc)
{
if (!(svc->flags & SVC_DISABLED)) {
service_start(svc, NULL);
}
}
zygote 在 init.rc 中的内容如下,由于 classname 为 main,和上述第二个 command 的参数相同,而且 flags 不为 SVC_DISABLED,因此系统开始调用 service_start 函数来启动 zygote。
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
根据 Android 初始化语言的语法,可以知道 zygote 服务进程的名字是 zygote ,执行程序路径为
/system/bin/app_process
,参数为-Xzygote /system/bin --zygote --start-system-server
。创建一个名字为 zygote 的 socket,这个 socket 用于接收 ActivityManagerService 发送过来的新建应用程序进程的请求。zygote 进程重启时会往 sysfs 中写入命令,同时会重启 media 服务和 netd 服务。
service_start 函数首先使用 fork 函数克隆出一个子进程。然后在子进程中创建 socket ,并在环境变量中设置创建的 socket 信息。最后调用系统函数 execve 开始执行 /system/bin/app_process
程序。
代码路径:system/core/init/init.c
void service_start(struct service *svc, const char *dynamic_args)
{
struct stat s;
pid_t pid;
int needs_console;
int n;
char *scon = NULL;
int rc;
/* starting a service removes it from the disabled or reset * state and immediately takes it out of the restarting * state if it was in there */
svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART));
svc->time_started = 0;
/* running processes require no additional work -- if * they're in the process of exiting, we've ensured * that they will immediately restart on exit, unless * they are ONESHOT */
if (svc->flags & SVC_RUNNING) {
return;
}
......
pid = fork();
if (pid == 0) {
......
for (si = svc->sockets; si; si = si->next) {
int socket_type = (
!strcmp(si->type, "stream") ? SOCK_STREAM :
(!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
int s = create_socket(si->name, socket_type,
si->perm, si->uid, si->gid);
if (s >= 0) {
publish_socket(si->name, s);
}
}
......
if (!dynamic_args) {
if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) { ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno)); } } ...... } ...... }
/system/bin/app_process
是 zygote 进程的可执行程序,其源代码位于 frameworks/base/cmds/app_process/app_main.cpp
文件,入口函数是 main。具体启动细节请参考老罗的文章 Android 系统进程 Zygote 启动过程的源代码分析。
4. 监听事件,重启服务
init 进程最终会进入一个无限循环。在这个无限循环中,init 进程调用系统函数 poll 监听三类事件。分别调用 get_property_set_fd 得到文件描述符 property_fd,调用 get_signal_fd 得到文件描述符 signal_fd ,get_keychord_fd 得到文件描述符 keychord_fd。这三个文件描述符的初始化过程之前通过 queue_builtin_action 添加进 action_queue,然后被触发执行。
queue_builtin_action(keychord_init_action, "keychord_init");
queue_builtin_action(property_service_init_action, "property_service_init");
queue_builtin_action(signal_init_action, "signal_init");
其中,property_fd 用于监听属性设置操作,signal_fd 用于获取子进程退出信号,keychord_fd 用于监听外设的组合按键。代码如下:
代码路径:system/core/init/init.c
int main(int argc, char **argv)
{
......
for(;;) {
int nr, i, timeout = -1;
execute_one_command();
restart_processes();
if (!property_set_fd_init && get_property_set_fd() > 0) {
ufds[fd_count].fd = get_property_set_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = 0;
fd_count++;
property_set_fd_init = 1;
}
if (!signal_fd_init && get_signal_fd() > 0) {
ufds[fd_count].fd = get_signal_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = 0;
fd_count++;
signal_fd_init = 1;
}
if (!keychord_fd_init && get_keychord_fd() > 0) {
ufds[fd_count].fd = get_keychord_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = 0;
fd_count++;
keychord_fd_init = 1;
}
......
nr = poll(ufds, fd_count, timeout);
if (nr <= 0)
continue;
for (i = 0; i < fd_count; i++) {
if (ufds[i].revents == POLLIN) {
if (ufds[i].fd == get_property_set_fd())
handle_property_set_fd();
else if (ufds[i].fd == get_keychord_fd())
handle_keychord();
else if (ufds[i].fd == get_signal_fd())
handle_signal();
}
}
}
return 0;
}
当监听到 property_fd 上发生了可读事件时,调用 handle_property_set_fd 函数处理事件。首先通过 socket 接收到客户端传过来的消息,然后根据消息类型设置属性或者启动相应的服务。
当监听到 signal_fd 上发生了可读事件时,调用 handle_signal 函数处理事件。通过 waitpid 得到子进程的 pid,对子进程做一些清理工作。然后根据子进程的启动配置参数判断是否需要重新,如果需要则设置其属性为 SVC_RESTARTING,最后在 restart_processes 中完成重启,过程如下所示:
代码路径:system/core/init/init.c
static void restart_processes()
{
process_needs_restart = 0;
service_for_each_flags(SVC_RESTARTING,
restart_service_if_needed);
}
static void restart_service_if_needed(struct service *svc)
{
time_t next_start_time = svc->time_started + 5;
if (next_start_time <= gettime()) {
svc->flags &= (~SVC_RESTARTING);
service_start(svc, NULL);
return;
}
if ((next_start_time < process_needs_restart) ||
(process_needs_restart == 0)) {
process_needs_restart = next_start_time;
}
}
当监听到 keychord_fd 上发生了可读事件时,调用 handle_keychord 函数处理事件。通过系统调用 read 从 keychord_fd 上读取 id,再根据 id 启动相应的 keychord 服务。
至此,Android 启动流程第二阶段完成,下文开始分析应用程序 Launcher 的启动过程。
参考学习资料
1. Understanding The Linux Kernel(Third Edition)