Android 启动优化系列 —— 系统启动流程

系统启动流程概述

  1. 按下开机键执行预定义代码(Boot ROM),加载引导程序 Boot Loader 并执行。
  2. Boot Loader 拉起并运行OS系统(Linux 内核)。
  3. 系统进行初始化操作(进行系统设置、挂载文件、加载各种驱动等操作)。
  4. 系统拉起 init 进程(系统起来后拉起的第一个用户态进程,汇编语言调用的),并进行初始化操作(启动属性服务、启动 zygote 进程、启动 Media Server 进程等等)。
  5. 启动 zygote 孵化器(加载JVM、JNI、创建服务端Socket、启动SystemServer进程)。
  6. SystemServer 启动一些列服务(AMS、WMS、PKMS、PMS 等等)。
  7. 启动 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 的锁在子进程就无法释放。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值