Android 启动优化 —— 系统启动流程
系统启动流程概述
- 按下开机键执行预定义代码(Boot ROM),加载引导程序 Boot Loader 并执行。
- Boot Loader 拉起并运行OS系统(Linux 内核)。
- 系统进行初始化操作(进行系统设置、挂载文件、加载各种驱动等操作)。
- 系统拉起 init 进程(系统起来后拉起的第一个用户态进程,汇编语言调用的),并进行初始化操作(启动属性服务、启动 zygote 进程、启动 Media Server 进程等等)。
- 启动 zygote 孵化器(加载JVM、JNI、创建服务端Socket、启动SystemServer进程)。
- SystemServer 启动一些列服务(AMS、WMS、PKMS、PMS 等等)。
- 启动 Launcher,app 启动时会从 zygote 进程 fork 出一个子进程(也就是启动的app的进程)。
系统启动流程相关源码
根据上面的系统启动流程概述中的步骤,结合源码来大概过一下流程,加深印象。
init 进程初始化操作
从第一个用户态进程 init 被拉起后的初始化操作开始跟踪 (在线看源码,下面的截图均来自于此),进入 init.cpp 的 main 方法
上面说到 init 进程会启动 zygote 进程等等,这个 LoadBootScripts 方法就是重点,进入 LoadBootScripts 方法:
这里会加载 init.rc ,下图这个文件:
打开查看一下,是一个 Android 初始化语言(Android Init Language)编写的脚本:
回过头看一下同目录下的 zygote 文件:
回过头到 init.rc 中看一下 zygote 触发的时机:
zygote 是在 late-init 触发器中启动的。看到这里后,再回过头继续看 LoadBootScripts 是如何解析 init.rc 的,回到 init.cpp 的 LoadBootScripts 方法:
点进去看一下 ParseConfig 的源码:
这里就是一个找文件的方法,最终会走到 ParseConfigFile 方法:
再点击去看一下 ParseData 方法:
end_selection 又调用了 EndSection() :
以 Zygote 为例,Zygote 是一个 service 那么创建的解析器也就是 service.cpp,进入 service.cpp 查看 EndSection 方法:
可以看出解析出来的服务放进了一个容器中。
到这里为止,以及看到了 Zygote 是如何启动的,但是 init 进程中其他服务的启动还是没有看到。看一下 LoadBootScripts 上面的代码:
它有一个 cpp 文件:
在这里做了映射,前面 init.rc 文件中的 class_start 都会调用到映射中的 do_class_start 方法:
跟踪下 StartIfNotDisabled 方法看下服务是如何启动的:
点进去 Start 方法源码(源码较多,部分截取):
fork 了一下,这个 fork 操作的是 init 进程,fork 方法会返回两次,返回值分为三种情况: 等于0 子进程成功,接下来进入子进程执行流程;大于0,也是成功创建子进程,但会继续在父进程执行代码;小于0,则创建失败。所以,截图中下面的 if (pid == 0) 整个 if 包裹的代码都是在子进程中执行,在这个 if 的最后部分:
在这里调用execv函数启动\frameworks\base\cmds\app_process\app_main.cpp 的 main 函数,看一下源码(部分截取):
根据不同的参数去启动对应的服务。
Zygote 启动
根据上一节最后的调用流程,那么如果是 Zygote 启动,那么会调用到 AndroidRuntime 的 start 方法,并且穿入 ZygoteInit 的包名(Java文件),跟踪下 start 方法:
到这里,虚拟机已经被创建出来了,下面紧接着就是注册安卓相关功能(JNI):
startReg 主要是JNI 注册,比如安卓中常用的 new Thread 开启一个线程,最终是通过 JNI 调用到了 Native 的 pthread_create 。
下面在接着看一下 ZygoteInit 的 Main 方法:
preload 中包含一系列的初始化操作:
回过头接着看 main 方法:
SystemServer 启动
上一节 ZygoteInit 中 调用了 forkSystemServer 方法,那么这个方法肯定是启动 SystemServer 服务的,点进去源码:
又调用到了 handleSystemServerProcess 方法(只截取了最后部分):
又调用到了 ZygoteInit.zygoteInit 方法:
有调用到了 RuntimeInit.applicationInit 方法:
findStaticMain:
这里可以总结出,ZygoteInit Main 方法中的 forkSystemServer 方法最终返回的是 Runnable,且 run 方法实现了对 SystemServer 类 Main 方法的调用。回过头看一下 ZygoteInit Main 方法:
这里就对应上了,接着去看 SystemServer 的 main 方法:
比如 Android 的 AMS 就是在 startBootstarpServices 中启动:
其他服务还很多就不一一看了,回到 main 函数,服务都启动完成后:
执行到这里后 就该启动手机的 Launcher 程序,Launcher 本质就是一个 app,有关 AMS 启动流程在下篇博客继续分享。
延伸一下
启动一个App会 fork 一个 Zygote 进程,为什么不 fork init 进程 或是 SystemServer 进程?
回想一下 Zygote 进程 和 init 进程 SystemServer 进程 启动后的初始化操作对比,init 进程相比于 Zygote 进程会额外加载一些驱动,过多的初始化设置;SystemServer 进程相比于 Zygote 进程会启动 AMS WMS 等等等等几十个服务。另外 fork 多线程仅仅会将发起调用的线程拷贝到子进程,这个过程可能会导致死锁问题。
fork 过程中的死锁
假设 A 进程中有 t1,t2 两个线程,且 t1 持有 t2 的锁,t2 正在发生调用时对 A 进程进行 fokr,只会把 t2 线程拷贝到子线程,t1 线程会蒸发,那么 t2 的锁在子进程就无法释放。