Android - 开机启动 Init 进程
init介绍
当 Linux 内核启动后会初始化各种软硬件环境,加载驱动程序,挂载根文件系统,Linux 内核加载的准备完毕后就开始加载一些特定的程序(进程)了。第一个加载的就是 init 进程。
相关源码文件:
/system/core/init/Init.cpp
/system/core/rootdir/init.rc
/system/core/init/init_parser.cpp
/system/core/init/builtins.cpp
/system/core/init/signal_handler.cpp
Init 进程
我们应该都知道不管是 Java 还是 C/C++ 去运行某一个程序(进程)都是 XXX.xxx 的 main 方法作为入口,因此我们找到system/core/init/init.cpp 的 main() 方法:
int main(int argc, char** argv) {
...
if (is_first_stage) { //.创建目录,挂载分区
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);
}
...
// 初始化 signal handler
signal_handler_init();
// 解析 init.rc 脚本文件
init_parse_config_file("/init.rc");
// 将解析脚本中对应的操作添加到 action_queue 队列中
action_for_each_trigger("early-init", action_add_queue_tail);
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
queue_builtin_action(keychord_init_action, "keychord_init");
queue_builtin_action(console_init_action, "console_init");
...
// 这里是个死循环
while (true) {
if (!waiting_for_exec) {
// 执行命令
execute_one_command();
restart_processes();
}
...
}
return 0;
main 方法具体分为四个步骤:1.创建目录,挂载分区,2.解析启动脚本,3.启动解析的服务,4.守护解析的服务。init.rc 文件是 Android 系统的重要配置文件,位于 /system/core/rootdir/init.rc
init.rc文件
nit.rc 基本单位是 section。
section 有三种类型:
- on
- service
- import
on 类型
on 类型 表示一系列的命令组合,eg:
on init
mkdir /productinfo 0771 system system
# SPRD: modify for storage manage @{
mount tmpfs tmpfs /storage mode=0751,gid=1028
# @}
mkdir /mnt/media_rw/sdcard0 0700 media_rw media_rw
mkdir /storage/sdcard0 0700 root root
mkdir /mnt/shell/emulated 0700 shell shell
mkdir /storage/emulated 0555 root root
# SPRD: move this to board level init @{
#export EXTERNAL_STORAGE /storage/emulated/legacy
#export SECONDARY_STORAGE /storage/sdcard0
# @}
# SPRD: for storage manage @{
export LEGACY_STORAGE /storage/emulated/legacy
# @}
这样一个 section 里面包含了多个命令。命令的执行是以 section 为单位的。
上面这些命令都会一起顺序执行,不会单独执行。
他们的执行由 init.cpp 的 main() 决定。在当其中调用
action_for_each_trigger("init", action_add_queue_tail);
时,就将 on init 开始的这样一个 section 里所有的命令加入到一个执行队列,在未来某个时候会顺序执行队列里的命令。
service 类型
service 类型 的section 表示一个可执行的程序,下面是个人工作内容中片段代码:
例一:
service gocsdk /system/bin/gocsdk
class main
disabled
oneshot
on property:sys.boot_completed=1
chmod 777 /dev/goc_serial
start gocsdk
gocsdk 作为一个名字标识了这个 service,这个可执行程序的位置在 /system/bin/gocsdk。
下面的 disable、oneshot 被称为 options,options 是用来描述的 service 的特点,不同的 service 有不同的 options。
service 的执行时间是在property:sys.boot_completed=1 这个命令被执行的时候,这个命令总存在于某个 on 类型的section 中作为一个服务启动的动作(Action)。
就拿上面的代码来说property:sys.boot_completed=1满足条件被执行时,则会启动这个类型为 main的 gocsdk service。
例二:
service yo_service1 /system/bin/yo_service1
class core
user system
disabled
group system radio shell
oneshot
on yo_fs
class_start core
其中 yo_service1 这个 service 的类型是 core。
在 yo_fs 被调用的时候则将会 class_start 而执行所有 类型为 core 的 service。
import 类型
import 类型表示包含了另外一些 section,在解析完 init.rc 后会继续调用 init_parse_config_file 来解析引入的 .rc 文件。
eg:
比如我们在 init.sc8830.rc 的开始可以看到
import /init.usb.rc
import /init.trace.rc
表示在运行完本 rc 后还将继续运行 init.usb.rc 和 init.trace.rc。
解析 init.rc 的过程就是识别一个个 section 的过程。
在 init.c 中的 main() 中去执行一个个命令。
(android采用双向链表来存储section的信息,解析完成之后,会得到三个双向链表action_list、service_list、import_list来分别存储三种section的信息上。)
加到可执行队列
将解析后的文件内容加到可执行队列中去,如下:
action_for_each_trigger("early-init", action_add_queue_tail);
1083
1084 // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
1085 queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
1086 // ... so that we can start queuing up actions that require stuff from /dev.
1087 queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
1088 queue_builtin_action(keychord_init_action, "keychord_init");
1089 queue_builtin_action(console_init_action, "console_init");
1090
1091 // Trigger all the boot actions to get us started.
1092 action_for_each_trigger("init", action_add_queue_tail);
1093
1094 // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
1095 // wasn't ready immediately after wait_for_coldboot_done
1096 queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
1097
1098 // Don't mount filesystems or start core system services in charger mode.
1099 char bootmode[PROP_VALUE_MAX];
1100 if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {
1101 action_for_each_trigger("charger", action_add_queue_tail);
1102 } else {
1103 action_for_each_trigger("late-init", action_add_queue_tail);
1104 }
while (true) {
1110 if (!waiting_for_exec) {
1111 execute_one_command(); //
1112 restart_processes();
1113 }
1114
1115 int timeout = -1;
1116 if (process_needs_restart) {
1117 timeout = (process_needs_restart - gettime()) * 1000;
1118 if (timeout < 0)
1119 timeout = 0;
1120 }
1121
1122 if (!action_queue_empty() || cur_action) {
1123 timeout = 0;
1124 }
1125
1126 bootchart_sample(&timeout);
1127
1128 epoll_event ev;
1129 int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
1130 if (nr == -1) {
1131 ERROR("epoll_wait failed: %s\n", strerror(errno));
1132 } else if (nr == 1) {
1133 ((void (*)()) ev.data.ptr)();
1134 }
1135 }
1136
启动服务
接下来看下是如何执行的,这里我们主要了解服务是如何启动的:
void execute_one_command() {
Timer t;
char cmd_str[256] = "";
char name_str[256] = "";
if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
cur_action = action_remove_queue_head();
cur_command = NULL;
if (!cur_action) {
return;
}
build_triggers_string(name_str, sizeof(name_str), cur_action);
INFO("processing action %p (%s)\n", cur_action, name_str);
cur_command = get_first_command(cur_action);
} else {
cur_command = get_next_command(cur_action, cur_command);
}
if (!cur_command) {
return;
}
// 服务的 func 跳转到 do_class_start,再到service_for_each_class
int result = cur_command->func(cur_command->nargs, cur_command->args);
...
}
//Service的启动是在do_class_start函数中完成:
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;
}
//遍历所有名称为classname,状态不为SVC_DISABLED的Service启动
void service_for_each_class(const char *classname,void (*func)(struct service *svc))
{
struct listnode *node;
struct service *svc;
list_for_each(node, &service_list) {
svc = node_to_item(node, struct service, slist);
// 如果名字是一致的执行 func ,这是一个回调函数也就是 service_start_if_not_disabled
if (!strcmp(svc->classname, classname)) {
func(svc);
}
}
}
static void service_start_if_not_disabled(struct service *svc)
{
if (!(svc->flags & SVC_DISABLED)) {
service_start(svc, NULL);
} else {
svc->flags |= SVC_DISABLED_START;
}
}
void service_start(struct service *svc, const char *dynamic_args)
{
...
// fork 创建了一个子进程
pid_t pid = fork();
if (pid == 0) {
// 代表子进程,子进程会继承父进程的所有资源,可简单理解为读时共享写时复制
...
// exc 函数族,这些都是 linux 基础知识
execve(svc->args[0], (char**) arg_ptrs, (char**) ENV); //asgs[0]表示可执行文件,后面是参数,注意fork里执行execves,会一直执行,不会返回fork中执行后面代码了。
}
}
do_class_start对应的命令:
KEYWORD(class_start, COMMAND, 1, do_class_start)
init.rc文件中搜索class_start:class_start main 、class_start core、……
main、core即为do_class_start参数classname
init.rc文件中Service class名称都是main:
ervice drm /system/bin/drmserver
class main
service surfaceflinger /system/bin/surfaceflinger
class main
于是就能够通过main名称遍历到所有的Service,将其启动。
do_class_start调用:
init.rc中
on boot //action
class_start core //执行command 对应 do_class_start
class_start main
守护解析的服务
看下init 进程是如何守护子进程的:
void signal_handler_init() {
// Create a signalling mechanism for SIGCHLD.
int s[2];
// 创建 socket pair 用于通信
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
ERROR("socketpair failed: %s\n", strerror(errno));
exit(1);
}
// 当捕获信号SIGCHLD,则写入signal_write_fd
signal_write_fd = s[0];
signal_read_fd = s[1];
// Write to signal_write_fd if we catch SIGCHLD.
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = SIGCHLD_handler;
act.sa_flags = SA_NOCLDSTOP;
// 这里注册监听,SA_NOCLDSTOP 使 init 进程只有在其子进程终止时才会受到 SIGCHLD 信号,然后触发SIGCHLD_handler函数
sigaction(SIGCHLD, &act, 0);
// 进入 waitpid 来处理子进程是否退出的情况
reap_any_outstanding_children();
// 调用 epoll_ctl 方法来注册 epoll 的回调函数
register_epoll_handler(signal_read_fd, handle_signal);
}
//读取数据
static void handle_signal() {
// Clear outstanding requests.
char buf[32];
// 读取 signal_read_fd 中的数据,并放入 buf,这里读出并没有什么实际作用,只是用于阻塞等待,当fd中有数据写入时,就会解除阻塞
read(signal_read_fd, buf, sizeof(buf));
reap_any_outstanding_children();
}
//写入数据
static void SIGCHLD_handler(int) {
// 向signal_write_fd写入1,为了把fd唤醒
if (TEMP_FAILURE_RETRY(write(signal_write_fd, "1", 1)) == -1) {
ERROR("write(signal_write_fd) failed: %s\n", strerror(errno));
}
}
static void reap_any_outstanding_children() {
while (wait_for_one_process()) { }
}
static bool wait_for_one_process() {
int status;
//等待任意子进程,如果子进程没有退出则返回 0,否则则返回该子进程 pid。
pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
if (pid == 0) {
return false;
} else if (pid == -1) {
return false;
}
//根据 pid 查找到相应的 service
service* svc = service_find_by_pid(pid);
//当 flags 为 RESTART,且不是 ONESHOT 时, kill 进程组内所有的子进程或子线程
if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
kill(-pid, SIGKILL);
}
//移除当前服务 svc 中的所有创建过的 socket
for (socketinfo* si = svc->sockets; si; si = si->next) {
char tmp[128];
snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
unlink(tmp);
}
//当 flags 为 EXEC 时,释放相应的服务
if (svc->flags & SVC_EXEC) {
waiting_for_exec = false;
list_remove(&svc->slist);
free(svc->name);
free(svc);
return true;
}
...
// 对于 ONESHOT 服务,使其进入 disabled 状态
if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
svc->flags |= SVC_DISABLED;
}
// 禁用和重置的服务,都不再自动重启
//总结性说明:一个服务进程在init.rc中只要没有声明SVC_ONESHOT和SVC_RESET标志,当该进程死亡时,就会被重启;
if (svc->flags & (SVC_DISABLED | SVC_RESET)) {
svc->NotifyStateChange("stopped"); //设置相应的service状态为stopped,即对于服务不需要自动重启则设置属性"init.svc.{$service_name}" "stopped"
return true;
}
/**
如果一个服务进程带有SVC_CRITICAL标志,且没有SVC_RESTART标志,当它crash、
一定时间内重启的次数超过4此时,系统会自动重启并进入recovery模式
*/
time_t now = gettime();
if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {
if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
ERROR("critical process '%s' exited %d times in %d minutes; "
"rebooting into recovery mode\n", svc->name,
CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
return true;
}
} else {
svc->time_crashed = now;
svc->nr_crashed = 1;
}
}
// 如果svc服务中有onrestart选项,则遍历进程重启时需要执行的command列表,执行当前 service 中所有 onrestar t命令,这个就是重启了
struct listnode* node;
list_for_each(node, &svc->onrestart.commands) {
command* cmd = node_to_item(node, struct command, clist);
cmd->func(cmd->nargs, cmd->args);
}
// 设置相应的 service 状态为 restarting
svc->NotifyStateChange("restarting");
return true;
}
至此 init 进程已全部分析完毕,有四个步骤:1. 创建目录,挂载分区,2. 解析启动脚本,3. 启动解析的服务,4. 守护解析的服务。最需要注意的是 init 创建了 zygote(创建 App 应用的服务)、servicemanager (client 与 service 通信管理的服务)、surfaceflinger(显示渲染服务) 和 media(多媒体服务) 等 service 进程。