安卓系统从开机到桌面显示是一个长而复杂的流程,本文参考安卓源码记录安卓启动流程的梳理学习。(文章涉及的源码基于 Android 10.0)由于 Android 启动流程很长,所以分几篇来记录,本篇记录安卓第一个用户态进程 Init 进程的启动过程。
[TOC]
Init
进程是安卓系统启动的第一个用户态进程,其 PID 为1,是由内核态进程0 idle
启动,Init
是启动众多安卓系统服务进程的源头。
启动 Init
进程的流程包括以下几步:
- 用户长按电源键,固化在ROM中的
BOOT
加载引导程序Bootloader
到 RAM 中 - 运行
Bootloader
启动 Linux 内核系统,启动第一个内核态进程idle
,并初始化内核驱动程序。 - 由
idle
进程启动Init
进程,具体是执行到system/core/Init
目录下的main.cpp
下面将梳理下 system/core/Init/main.cpp
的执行流程。
Init 入口函数
int main(int argc, char** argv) {
56 if (!strcmp(basename(argv[0]), "ueventd")) {
57 return ueventd_main(argc, argv);
58 }
60 if (argc > 1) {
61 if (!strcmp(argv[1], "subcontext")) {
62 android::base::InitLogging(argv, &android::base::KernelLogger);
63 const BuiltinFunctionMap function_map;
64
65 return SubcontextMain(argc, argv, &function_map);
66 }
68 if (!strcmp(argv[1], "selinux_setup")) {
69 return SetupSelinux(argv);
70 }
72 if (!strcmp(argv[1], "second_stage")) {
73 return SecondStageMain(argc, argv);
74 }
75 }
77 return FirstStageMain(argc, argv);
78 }
- 传入参数为
ueventd
, 走eventd_main
流程。 - 传入参数为
subcontext
, 走SubcontextMain
流程。 - 传入参数为
selinux_setup
, 走SetupSelinux
流程。 - 传入参数为
second_stage
, 走SecondStageMain
流程。 - 默认无参数, 走
FirstStageMain
流程。
从 main
函数可以看出 Init
流程分为好几个过程,由函数的入参控制。从 idle
进程执行到 Init.main
时,不带入参,即默认首先执行 FirstStageMain
流程。(当然从函数名也能看出来)
FirstStageMain
从 main.cpp
执行到 first_state_init.cpp
中的 FirstStageMain
函数,进行 Init
的第一个过程。这个过程主要工作包括:
- 挂载设备节点
- 创建系统关键目录
- 初始化 Log 系统
这一阶段代码代码较多,可以自行查看源码FirstStageMain 函数源码。在 FirstStageMain
函数执行结束的时候,通过 execv
命令启动了 Init
流程第二个过程 SetupSelinux
。
int FirstStageMain(int argc, char** argv) {
103 ...
116 CHECKCALL(clearenv());
117 CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
118 // Get the basic filesystem setup we need put together in the initramdisk
119 // on / and then we'll let the rc file figure out the rest.
120 CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
121 CHECKCALL(mkdir("/dev/pts", 0755));
...
168 #undef CHECKCALL
173 InitKernelLogging(argv);
236 setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
238 const char* path = "/system/bin/init";
239 const char* args[] = {path, "selinux_setup", nullptr};
240 execv(path, const_cast<char**>(args));
246 return 1;
247 }
SetupSelinux
SetupSelinux
函数在 selinux.cpp
文件中,这个过程主要工作是初始化selinux
、加载 selinux
规则。selinux
是 Linux
中的一个安全控制模块,在这种访问控制体系的限制下, 进程只能访问那些在他的任务中所需要的文件。在 SetupSelinux
函数执行结束的时候同样通过 execv
命令启动了 Init
的第三个过程 SecondStageMain
。
int SetupSelinux(char** argv) {
520 InitKernelLogging(argv);
522 if (REBOOT_BOOTLOADER_ON_PANIC) {
523 InstallRebootSignalHandlers();
524 }
527 SelinuxSetupKernelLogging();
528 SelinuxInitialize();
538 const char* path = "/system/bin/init";
539 const char* args[] = {path, "second_stage", nullptr};
540 execv(path, const_cast<char**>(args));
546 return 1;
547 }
SecondStageMain
Init
第三个过程在 init.cpp
文件中,这个过程主要做了以下几类工作:
- 初始化并启动属性服务
- 初始化子进程终止处理函数
- 解析
init.rc
文件,在这期间启动了system server
进程 - 通过
ActionManager
执行一些开机前的准备工作,如显示静态的 Android 开机画面 - 循环等待新的
Action
的到来
至此,init
进程启动过程结束。
int SecondStageMain(int argc, char** argv) {
...
623 SetStdioToDevNull(argv);
624 InitKernelLogging(argv);
643 property_init();
...
679 Epoll epoll;
680 if (auto result = epoll.Open(); !result) {
681 PLOG(FATAL) << result.error();
682 }
684 InstallSignalFdHandler(&epoll);
686 property_load_boot_defaults(load_debug_prop);
687 UmountDebugRamdisk();
688 fs_mgr_vendor_overlay_mount_all();
689 export_oem_lock_status();
690 StartPropertyService(&epoll);
691 MountHandler mount_handler(&epoll);
692 set_usb_controller();
706 LoadBootScripts(am, sm);
...
730 am.QueueBuiltinAction(
731 [&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {
732 for (const auto& svc : ServiceList::GetInstance()) {
733 keychords.Register(svc->keycodes());
734 }
735 keychords.Start(&epoll, HandleKeychord);
736 return Success();
737 },
738 "KeychordInit");
739 am.QueueBuiltinAction(console_init_action, "console_init");
741 // Trigger all the boot actions to get us started.
742 am.QueueEventTrigger("init");
...
744
765 while (true) {
766 // By default, sleep until something happens.
767 auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
768
769 if (do_shutdown && !shutting_down) {
770 do_shutdown = false;
771 if (HandlePowerctlMessage(shutdown_command)) {
772 shutting_down = true;
773 }
774 }
775
776 if (!(waiting_for_prop || Service::is_exec_service_running())) {
777 am.ExecuteOneCommand();
778 }
779 if (!(waiting_for_prop || Service::is_exec_service_running())) {
780 if (!shutting_down) {
781 auto next_process_action_time = HandleProcessActions();
782
783 // If there's a process that needs restarting, wake up in time for that.
784 if (next_process_action_time) {
785 epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
786 *next_process_action_time - boot_clock::now());
787 if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
788 }
789 }
790
791 // If there's more work to do, wake up again immediately.
792 if (am.HasMoreCommands()) epoll_timeout = 0ms;
793 }
794
795 if (auto result = epoll.Wait(epoll_timeout); !result) {
796 LOG(ERROR) << result.error();
797 }
798 }
799
800 return 0;
801 }
属性服务
属性服务类似于 Windows 中的注册表功能,提供给上层应用记忆一些设置属性,并在启动应用时读取并生效。由于所有进程的属性键值对都存在一块内存中,所以对于读写权限的控制至关重要,避免跨进程修改属性。
Android 将属性键值对的管理统一交由 Init
进程,其他进程不能直接修改属性,而只能通过和 Init
进程通信来修改,这样 Init
进程就可以根据消息来源进行权限控制。
-
Init
进程通过非阻塞式Socket
接收其他进程修改属性的消息 -
Init
进程通过property_set
函数修改属性 - 系统属性分为普通属性和控制属性两种,控制属性用来执行一些命令,以 ctl. 开头。
-
Init
进程调用property_set
函数时,会先从属性存储空间查找该属性,如果有则更新内容,如果没有则新增属性。但是如果属性是以ro.
开头,则表明是只读属性,函数会直接返回。如果属性是以persist.
开头,则写入持久化属性。
init.rc 脚本解析
Init.rc
是由 Android 初始化语言编写的配置脚本文件,主要有 Action、Service、Command、Option、Import
五类语句。具体语法不是本文重点。
从 init.rc
代码中可以看到,这里会根据 ro.zygote
属性 import
不同的 init.zygotexx.rc 脚本。
在 system/core/rootdir
中可以看到总共有四个 zygote init
脚本文件,系统会根据 ro.zygote
属性解析执行相应 的文件,以启动 zygote
进程。
总结
Init
进程启动流程概括为以下几点:
- Linux 内核
idle
进程在启动过程中,调用到Init
进程的main()
方法。 -
main()
方法分为三个过程:- 过程①,调用
first_state_init.cpp
中的FirstStageMain()
方法,完成设备挂载、关键系统目录创建、Log初始化。 - 过程②,调用
selinux.cpp
中的SetupSelinux()
方法,初始化 Selinux,加载 Selinux 规则。 - 过程③,调用
init.cpp
中的SecondStageMain()
方法,初始化并启动属性服务、加载启动相关.rc
脚本。在这个过程中,通过init.zygotexx.rc
启动了安卓系统中进程之父zygote
进程。
- 过程①,调用