栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

Android 启动优化 —— 系统启动流程
  • 系统启动流程概述
  • 系统启动流程相关源码
      • init 进程初始化操作
      • Zygote 启动
      • SystemServer 启动
  • 延伸一下
      • 启动一个App会 fork 一个 Zygote 进程,为什么不 fork init 进程 或是 SystemServer 进程?
      • fork 过程中的死锁

系统启动流程概述
  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函数启动frameworksbasecmdsapp_processapp_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 的锁在子进程就无法释放。

转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/1039396.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号