一、init 进程
init 进程是 Android 系统中用户进程的第一个进程,进程号为 1,被赋予很多极其重要的职责,如创建 Zygote 进程和属性服务等。init 进程由多个源文件组成,文件位于 system/core/init 目录。
二、init 进程启动流程
Android系统启动流程的前几步:
-
启动电源以及系统启动
按下电源时,引导芯片代码从预定义的地方(固化在 ROM)开始执行,加载引导程序 BootLoader 到 RAM 中,然后执行。
-
引导程序 BootLoader
是 Android 操作系统运行之前的一个小程序,把系统 OS 拉起来并运行。
-
Linux 内核启动
当内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。在内核完成系统设置后,首先在系统文件中寻找 init.rc 文件,并启动 init 进程。
-
init 进程启动
主要用来初始化和启动属性服务,也用来启动 Zygote 进程。
三、init入口函数
Linux 内核加载完成之后,第一件事就是启动 init 进程,首先会在系统文件中寻找 init.rc 文件
文件位置:system/core/init/init.cpp。
init.cpp 的入口函数 main() 方法代码流程:
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}
umask(0);
add_environment("PATH", _PATH_DEFPATH);
bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
//① 创建和挂载启动所需的文件目录
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);
#define MAKE_STR(x) __STRING(x)
mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
mount("sysfs", "/sys", "sysfs", 0, NULL);
}
open_devnull_stdio();
klog_init();
klog_set_level(KLOG_NOTICE_LEVEL);
NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
if (!is_first_stage) {
// Indicate that booting is in progress to background fw loaders, etc.
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
//② property_init():对属性进行初始化
property_init();
process_kernel_dt();
process_kernel_cmdline();
export_kernel_boot_props();
}
...
//③ start_property_service():启动属性服务
start_property_service();
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
Parser& parser = Parser::GetInstance();
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
parser.AddSectionParser("on", std::make_unique<ActionParser>());
parser.AddSectionParser("import", std::make_unique<ImportParser>());
//④ parse.ParseConfig("/init.rc"):解析 init.rc 配置文件
parser.ParseConfig("/init.rc");
...
while (true) {
if (!waiting_for_exec) {
am.ExecuteOneCommand();
//⑤ restart_processes():重启死去的进程
restart_processes();
}
int timeout = -1;
if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if (timeout < 0)
timeout = 0;
}
if (am.HasMoreCommands()) {
timeout = 0;
}
bootchart_sample(&timeout);
epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
if (nr == -1) {
ERROR("epoll_wait failed: %s\n", strerror(errno));
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
}
return 0;
}
在 main() 函数中,②③ 分别完成了属性服务的初始化和启动,④ 用于解析 init.rc 文件,① 用于挂载系统运行时目录(系统停止时消失)。
四、解析 init.rc 文件
文件位置:system/core/init/init_parse.cpp
init.rc 是由 Android 初始化语言编写的一个非常重要的配置脚本文件。Android 初始化语言主要包含 5 种类型的语句:Action、Service、Command、Option、Import
Android 8.0 对 init.rc 文件进行了拆分,每个服务对应一个 rc 文件,其中 Zygote 启动脚本在 init.ZygoteXX.rc 中定义,64 位处理器对应 init.Zygote64.rc,对应文件目录 system/core/rootdir/init.zygote64.rc
在启动 Zygote 之前 首先会解析 Service 类型语句,把 Service 对象加入到 Service 链表中
init 启动 Zygote 会遍历 Service 链表,找到 class_name 为 main 的 Service,其中:
① Zygote 的 classname 为 main
② Zygote 执行程序的路径为 /system/bin/app_process64,对应文件为 app_main.cpp
init.rc 文件最后会调用 Service#Start() 函数(函数所在文件为 system/core/init/service.cpp),如果 Service 已经运行,则不再启动,否则最终通过会 execve(…) 函数去启动子进程,这样 Service 子进程就会被启动,如果当前启动的 Service 是 Zygote,则会进入 app_main.cpp 的 mian() 函数,也就是在 Zygote 的 main() 函数,接着就会调用 AppRuntime#start() 方法启动 Zygote,至此 Zygote 就启动了。
五、属性服务
Windows平台上有一个注册表管理器,注册表的内容采用键值对的形式来记录用户、软件的一些使用信息。即使系统或者软件重启,它还是能够根据之前在注册表中的记录,进行相应的初始化工作。Android也提供了一个类似的机制,叫做属性服务。
六、init进程总结
讲到这,总结起来init进程主要做了三件事: 1.创建一些文件夹并挂载设备 2.初始化和启动属性服务 3.解析init.rc配置文件并启动zygote进程
参考资料: