第三章 Android系统启动流程
面试题预览
- 你了解Android系统启动流程吗?⭐⭐⭐⭐⭐
- system_server 为什么要在 Zygote 中启动,而不是由 init 直接启动呢?⭐⭐⭐
- 能说说具体是怎么导致死锁的吗?⭐⭐⭐
- Zygote 为什么不采用 Binder 机制进行 IPC 通信?⭐⭐⭐⭐
- 请简述从点击图标到app启动的流程。⭐⭐⭐⭐⭐
- 说说Activity加载的流程。⭐⭐⭐⭐⭐
- Zygote为什么需要用到Socket通讯而不是Binder?⭐⭐⭐⭐
- Zygote进程最原始的进程是什么进程(或者Zygote进程是怎么来的)?⭐⭐⭐
1 概述
Android系统启动流程图
在init进程启动前,如上面“Android系统启动流程图”所示:
1)按Power键启动电源及系统启动
当按下电源键,引导芯片代码开始从固化在ROM中预定义的地方开始执行,加载引导程序Bootloader到RAM,然后执行引导程序。
2)引导程序Bootloader
引导程序BootLoader是Android系统运行前的第一个程序,它的主要作用是把系统OS拉起来并运行
3)Linux内核启动
Linux kernel启动. kernel(内核)启动时会设置缓存、被保护存储器、计划列表、加载驱动.然后在系统文件中寻找init文件,并启动init进程
4)init进程启动
详细见本章第二节分析。
2 Init启动进程详解
2.1 init进程概述
在运行Android程序后首先会启动init 进程,此进程是Linux 系统中用户空间的第一个进程,进程编号为1。init被赋予了很多重要的职责,比如我们熟悉的Zygote孵化器进程就是由init进程启动的。
2.2 启动前分析
在用户空间启动init进程,再通过init进程,来读取init.rc中的相关配置,从而来启动其他相关进程以及其他操作。
init进程被赋予了很多重要工作,init进程启动主要分为两个阶段:
第一个阶段完成以下内容:
- ueventd/watchdogd跳转及环境变量设置
- 挂载文件系统并创建目录
- 初始化日志输出、挂载分区设备
- 启用SELinux安全策略
- 开始第二阶段前的准备
第二个阶段完成以下内容:
- 初始化属性系统
- 执行SELinux第二阶段并恢复一些文件安全上下文
- 新建epoll并初始化子进程终止信号处理函数
- 设置其他系统属性并开启属性服务
Android 是一个基于Linux 内核的系统,与Linux、Fedora Linux最大的区别是,Android在应用层专门为移动设备添加了一些特有的支持。目前 Linux 有很多通信机制可以在用户空间和内核空间之间交互,例如设备驱动文件(位于/dev目录中)、内存文件(/proc、/sys 目录等)。Android在加载Linux基本内核后,就开始运行一个初始化进程init。从Android加载Linux内核时设置了如下参数。
Kemelcommand line: noinitrd root=/dev/nfs console=ttySACO init=/initnfsroot=192.168.1.103:/nfsbootip=192.168.1.20:192.168.1.103:192.168.1.1:255.255.255.0::eth0:on
在上述命令中,Android系统一般会在根目录下放一个init的可执行文件,也就是说Linux系统的init进程在内核初始化完成后,就直接执行init这个文件。Init 进程的代码位于源码的目录 system/core/init,在分析 init 的核心代码之前,还需要做如下工作。
- 初始化属性。
- 处理配置文件的命令(主要是init.rc 文件),包括处理各种 Action。
- 性能分析(使用b
- ootchart 工具)。
- 无限循环执行 command(启动其他的进程)。
2.3 init进程启动过程代码分析
init程序并不是由一个源代码文件组成的,而是由一组源代码文件的目标文件链接而成的。这些文件位于目录/system/core/init 中,例如system/core/init/init.cpp。 主要的 JNI代码放在路径frameworks/base/core/jni/中。
init进程的入口函数
在Linux内核加载完成之后,它首先在系统文件中寻找init.rc文件,并启动init进程,然后查看init进程的入口函数main,代码如下所示:
system/core/init/init.cpp
int main(int argc, char** argv) {
// ... 1
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
// ... 2
if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}
// ... 3
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
// 添加环境变量
add_environment("PATH", _PATH_DEFPATH);
// 获取本次启动是否是系统启动的第一阶段,如果是第一阶段,
// 则进入下面的if语句中,创建并挂载相关的文件系统。
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
//创建文件并挂载
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));
// Don't expose the raw commandline to unprivileged processes.
chmod("/proc/cmdline", 0440);
gid_t groups[] = { AID_READPROC };
setgroups(arraysize(groups), groups);
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
// 初始化Kernel的Log,这样就可以从外界获取Kernel的日志
InitKernelLogging(argv);
LOG(INFO) << "init first stage started!";
...
// Set up SELinux, loading the SELinux policy.
// 启动SELinux。
selinux_initialize(true);
...
// 初始化属性服务
property_init(); // ....... 5
// If arguments are passed both on the command line and in DT,
// properties set in DT always have priority over the command-line ones.
process_kernel_dt();
process_kernel_cmdline();
// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
export_kernel_boot_props();
// Make the time that init started available for bootstat to log.
property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
// Set libavb version for Framework-only OTA match in Treble build.
const char* avb_version = getenv("INIT_AVB_VERSION");
if (avb_version) property_set("ro.boot.avb_version", avb_version);
// Clean up our environment.
unsetenv("INIT_SECOND_STAGE");
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");
// Now set up SELinux for second stage.
selinux_initialize(false);
selinux_restore_context();
// 创建epoll句柄
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
PLOG(ERROR) << "epoll_create1 failed";
exit(1);
}
// 用于设置子进程信号处理函数,如果子进程(Zygote进程)异常退出,init进程会调用该函数中
// 设定的信号处理函数来进行处理
signal_handler_init(); // ...... 6
// 导入默认的环境变量
property_load_boot_defaults();
export_oem_lock_status();
// 启动属性服务
start_property_service(); // ...... 7
set_usb_controller();
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
ActionManager& am = ActionManager::GetInstance();
ServiceManager& sm = ServiceManager::GetInstance();
Parser& parser = Parser::GetInstance();
parser.AddSectionParser("service", std::make_unique(&sm));
parser.AddSectionParser("on", std::make_unique(&am));
parser.AddSectionParser("import", std::make_unique(&parser));
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
// 解析init.rc配置文件
parser.ParseConfig("/init.rc"); // ...... 8
parser.set_is_system_etc_init_loaded(
parser.ParseConfig("/system/etc/init"));
parser.set_is_vendor_etc_init_loaded(
parser.ParseConfig("/vendor/etc/init"));
parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
} else {
parser.ParseConfig(bootscript);
parser.set_is_system_etc_init_loaded(true);
parser.set_is_vendor_etc_init_loaded(true);
parser.set_is_odm_etc_init_loaded(true);
}
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
if (false) DumpState();
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");
// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
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 || sm.IsWaitingForExec())) {
// 遍历执行每个action中携带的command对应的执行函数
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || sm.IsWaitingForExec())) {
if (!shutting_down)
// 重启死去的进程
restart_processes(); // ...... 9
// If there's a process that needs restarting, wake up in time for that.
if (process_needs_restart_at != 0) {
epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
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;
}
另外,还涉及了其他目录中的如下文件。
- 涉及源码文件:
- /system/core/init/main.cpp
- /system/core/init/init.cpp
- /system/core/init/ueventd.cpp
- /system/core/init/selinux.cpp
- /system/core/init/subcontext.cpp
- /system/core/base/logging.cpp
- /system/core/init/first_stage_init.cpp
- /system/core/init/first_stage_main.cpp
- /system/core/init/first_stage_mount.cpp
- /system/core/init/keyutils.h
- /system/core/init/property_service.cpp
- /external/selinux/libselinux/src/label.c
- /system/core/init/signal_handler.cpp
- /system/core/init/service.cpp
3 Zygote进程详解
在 Android系统中有如下3个十分重要的进程系统。
- Zygote 进程: 被称为"孵化"进程或"孕育"进程,功能和Linux系统的fork类似,用于"孕 育"产生出不同的子进程。
- System进程∶系统进程,是整个 Android Framework所在的进程,用于启动 Android 系统。其 核心进程是 system server,其父进程就是Zygote。
- 应用程序进程∶每个 Android 应用程序运行后都会拥有自己的进程,这和Windows 资源管理器中体现的进程是同一含义。
本节接下来将详细分析 Zygote 进程系统的基本知识。
3.1 Zygote介绍
我们的Android系统是基于Linux系统,所以当我们开机的时候,第一个启动的是Init进程,而后面所有的进程都是Init的子进程,Zygote就是Init进程通过解析init.rc文件之后,启动的一个进程。Zygote是Android系统的核心进程之一,被认为是Framework大家族的祖先。事实上,JavaVM(Java 虚拟机)、应用程序进程以及运行系统关键服务的 SystemServer 进程都是由 Zygote 来创建的。从总体架构上看, Zygote是一个简单的典型 C/S 结构。其他进程作为一个客户端向 Zygote发出"孕育"请求,当Zygote 接收到命令后就"孕育"出一个 Activity 进程。
这里回答本地的面试题1(Zygote为什么需要用到Socket通讯而不是Binder?)
- 先后时序问题:zygote比service manager先启动,而Binder引用都是保存在ServiceManager进程中的,从这个意义上,他没有service manager可以注册,所以没法用binder。
- 多线程问题:zygote是通过fork生成进程的,而多线程是不允许使用fork的,否则可能造成死锁,同时binder又是多线程的,所以干脆不同binder而使用socket。
- 效率问题:AMS和Zygote之间使用的LocalSocket,相对于网络Socket,减少了数据验证等环节,所以其实效率相对于正常的网络Socket会大幅的提升。虽然还是要经过两次拷贝,但是由于数据量并不大,所以其实影响并不明显。所以,LocalSocket效率其实也不低
- 安全问题:LocalSocket其实也有权限校验,并不意味着可以被所有进程随意调用
Zygote的进程的主要作用只有两个,可见下图:
- 启动SystemServer进程。SystemServer是一个用于启动手机内部各种服务的进程,我们常说的PMS,AMS等都是由SystemServer所启动。
- 在系统运行过程中,即时的去孵化APP进程,也就是我们每次点击APP图标启动的APP的时候,zygote就开始运作了。
3.2 Zygote的启动流程
3.2.1 概述
Zygote进程启动中承担的主要职责如下:
- 创建AppRuntime,执行其start方法。
- 创建JVM并为JVM注册JNI方法。
- 使用JNI调用ZygoteInit的main函数进入Zygote的Java FrameWork层。
- 调用preload完成系统资源的预加载。主要包括preloadClasses,preloadResources,preloadDrawables,preloadSharedLibraries
- 通过ZygoteServer创建本地服务端socke连接(LocalServerSocket), 调用Os.socket, bindLocal绑定socket文件和进程
- 启动SystemServer进程。
- 通过runSelectLoop调用linux poll机制,阻塞等待客户端请求连接,创建应用,关闭连接请求
具体分析如下:
如下图,Zygote的主要启动流程为:
3.2.2 init.rc
在init中通过init进程解析zytoe.rc中的配置执行脚本,通过FindService方法找到rc中配置的zygote服务, fork出zygote进程在fork出的zygote进程中,执行service对应的/system/bin/app_process64脚本,进入app_main.cpp对应的main方法;
详情参考: segmentfault.com/a/119000002…
3.2.3 app_main.main()
-
- 解析main方法的调用参数,判断当前所处的进程模式
- 如果是处于zygote进程,则完成zygote的初始化,并创建systemserver进程
- 如果是处于应用进程,则完成运行时初始化
3.2.4 ZygoteInit初始化进程
4 APP启动流程
4.1 概述
从用户手指点击桌面上的应用图标到屏幕上显示出应用主Activity界面而完成应用启动,快的话往往都不需要一秒钟,但是这整个过程却是十分复杂的,其中涉及了Android系统的几乎所有核心知识点。同时应用的启动速度也绝对是系统的核心用户体验指标之一,多少年来,无论是谷歌或是手机系统厂商们还是各个Android应用开发者,都在为实现应用打开速度更快一点的目标而不断努力。但是要想真正做好应用启动速度优化这件事情,我想是必须要对应用启动的整个流程有充分的认识和理解的.
可以先看下下面的启动流程图:
4.2 APP启动流程
- 点击桌面APP图标时,Launcher通过Binder通信,调用system_server进程中AMS服务的startActivity方法,发起启动请求
- system_server进程接收到请求后,向Zygote进程发送创建进程的请求
- Zygote进程fork出App进程,并执行ActivityThread的main方法,创建ActivityThread线程,初始化MainLooper,主线程Handler,同时初始化ApplicationThread用于和AMS通信交互
- App进程,通过Binder向sytem_server进程发起attachApplication请求,这里实际上就是APP进程通过Binder调用sytem_server进程中AMS的attachApplication方法,上面我们已经分析过,AMS的attachApplication方法的作用是将ApplicationThread对象与AMS绑定
- system_server进程在收到attachApplication的请求,进行一些准备工作后,再通过binder IPC向App进程发送handleBindApplication请求(初始化Application并调用onCreate方法)和scheduleLaunchActivity请求(创建启动Activity)
- App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送BIND_APPLICATION和LAUNCH_ACTIVITY消息,这里注意的是AMS和主线程并不直接通信,而是AMS和主线程的内部类ApplicationThread通过Binder通信,ApplicationThread再和主线程通过Handler消息交互。 ( 这里猜测这样的设计意图可能是为了统一管理主线程与AMS的通信,并且不向AMS暴露主线程中的其他公开方法,大神可以来解析下)
- 主线程在收到Message后,创建Application并调用onCreate方法,再通过反射机制创建目标Activity,并回调Activity.onCreate等方法
- 到此,App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染后显示APP主界面。