笔者希望做一个系列,整理 Android 基础技术,本章是关于 Framework
简述 Android 系统启动流程
当按电源键触发开机,首先会从 ROM 中预定义的地方加载引导程序 BootLoader 到 RAM 中,并执行 BootLoader 程序启动 Linux Kernel, 然后启动用户级别的第一个进程: init 进程(汇编调用)。init 进程会解析 init.rc 脚本做一些初始化工作,包括挂载文件系统、创建工作目录以及启动系统服务进程等,其中系统服务进程包括 Zygote、service manager、media 等(C++层的)。在 Zygote 中会进一步去启动 system_server进程(JAVA 层),然后在 system_server 进程中会启动 AMS、WMS、PMS 等服务,等这些服务启动之后,AMS 中就会打开 Launcher 应用的 home Activity,最终就看到了手机的 "桌面"。
Android 系统中有多少种不同的进程
- init进程 :用户态的第一个进程。解析init.rc创建各自服务进程(media, 守护进程),其中有一个叫zygote进程
- zygote进程 : 一个进程所需要的必要资源 preloadclass, 虚拟机vm
- system_server进程: 从zygote进程fork ,创建大量的服务(AMS,PMS,WMS等),加载Android framework所需要的资源context。
- app进程:从zygote进程fork
init 进程是什么,作用是什么
init进程是Linux系统中用户空间的第一个进程,进程号固定为1, 是C++进程. 由kernel启动
Kernel启动后,在用户空间启动init进程,并调用init中的main()方法执行init进程的职责。
1. 创建mkdir和挂载mount启动所需要的文件目录
- 初始化和启动属性服务
- 解析init.rc配置文件并启动Zygote进程,是由init进程fork出来的。
zygote进程是什么,作用是什么
Zgyote 是Android中的第一个art虚拟机 Android Runtime,他通过socket的方式与其他进程进行通信。
作用1 zygote fork的第一个java进程 SystemServer 进程
SystemServer 会开启一系列服务:AMS, WMS, PMS, PKMS等等
作用2从zygote fork子进程app Launcher进程
Launcher->app>fork
app_main.cpp做了什么,或者另外一种说法:Zygote是如何被启动的?
创建zygote进程,走到app_main.cpp的main方法,它是zygote进程的入口
app_main.cpp的main方法流程如下:
- 首先是Native层 startVm 启动了虚拟机!!! 这里就是启动JAVA虚拟机
- 然后 调用startReg函数为java虚拟机注册JNI方法
- 通过jni调用走到ZygoteInit.main,ZygoteInit.main() 是java层方法
- registerZygoteSocket() 注册一个Socket Server接收AMS请求,socketName以ANDROID_SOCKET_开头;
- preload(),预加载资源,例如常用类、颜色、drawable、JNI函数;以预加载类为例,读取/system/etc/preloaded-classes文件中配置的类名,通过Class.forName反射加载,常用类Activity、intent、String、Integer、TextView、Button;
- startSystemServer() ,内部通过Zygote.forkSystemServer启动SystemServer
- runSelectLoop() 循环等待处理请求;
system_server 为什么要在 Zygote 中启动,而不是由 init 直接启动呢?
Zygote 作为一个孵化器,可以提前加载一些资源,这样 fork() 时基于 Copy-On-Write 机制创建的其他进程就能直接使用这些资源,而不用重新加载。比如 system_server 就可以直接使用 Zygote 中的 JNI 函数、共享库、常用的类、以及主题资源。
使用 Zygote 进程去孵化应用进程有什么好处,为什么不是让 system_server 去孵化?
好处:应用在启动的时候需要做很多准备工作,包括启动虚拟机,加载各类系统资源等等,这些都是非常耗时的,如果能在zygote里就给这些必要的初始化工作做好,子进程在fork的时候就能直接共享,那么这样的话效率就会非常高。
为什么不是 system_server 进行 fork:
- 首先 system_server 相比 Zygote 多运行了 AMS、WMS、PMS 等服务,这些对一个应用程序来说是不需要的。
- 另外进程的 fork() 对多线程不友好,仅会将发起调用的线程拷贝到子进程,这可能会导致死锁,而 system_server 中肯定是有很多线程的。
Zygote 为什么不采用 Binder 机制进行 IPC 通信?
- 第一个原因,我们可以设想一下采用binder调用的话该怎么做,首先zygote要启用binder机制,需要打开binder驱动,获得一个描述符,再通过mmap进行内存映射,还要注册binder线程,这还不够,还要创建一个binder对象注册到serviceManager,另外AMS要向zygote发起创建应用进程请求的话,要先从serviceManager查询zygote的binder对象,然后再发起binder调用,这来来回回好几趟非常繁琐,相比之下,zygote和SystemServer进程本来就是父子关系,对于简单的消息通信,用管道或者socket非常方便省事。
- 第二个原因,如果zygote启用binder机制,再fork出SystemServer,那么SystemServer就会继承了zygote的描述符以及映射的内存,这两个进程在binder驱动层就会共用一套数据结构,这显然是不行的,所以还得先给原来的旧的描述符关掉,再重新启用一遍binder机制,这个就是自找麻烦了。
- 第三个原因:Binder 机制中存在 Binder 线程池,是多线程的,如果 Zygote 采用 Binder 的话就存在上面说的 fork() 与 多线程的问题了。其实严格来说,Binder 机制不一定要多线程,所谓的 Binder 线程只不过是在循环读取 Binder 驱动的消息而已,只注册一个 Binder 线程也是可以工作的,比如 service manager 就是这样的。实际上 Zygote 尽管没有采取 Binder 机制,它也不是单线程的,但它在 fork() 前主动停止 了其他线程,fork() 后重新启动了
多线程进程的fork调用会有什么问题?
会发生死锁
在POSIX 标准中,fork 的行为是这样的:复制整个用户空间的数据(通常使用 copy-on-write 的策略, 所以可以实现的速度很快)以及所有系统对象,然后仅复制当前线程到子进程。这里:所有父进程中别的线程,到了子进程中都是突然蒸发掉的。
假设这么一个环境,在 fork 之前,有一个子线程 lock 了某个锁,获得了对锁的所有权。fork 以 后,在子进程中,所有的额外线程都人间蒸发了。而锁却被正常复制了,在子进程看来,这个锁没有主人,所以没有任何人可以对它解锁。当子进程想 lock 这个锁时,不再有任何手段可以解开 了。程序发生死锁。
SystemServiceManager和service_manager的区别
- SystemServiceManager 专门管理各种服务的启动 java层的各种服务:AMS, PMS, WMS。SystemServiceManager 的ArrayList<SystemService> mServices 添加上面的服务
- service_manager 是C++层的 它是0号 binder服务。
如果我们启动一个hello World安卓用于程序,里面不另外启动其他线程,这个里面最少要启动多少个线程
启动4个 线程
- main线程,只是程序的主线程,也是日常用到的最多的线程,也叫UI线程,因为android的组件是非线程安全的,所以只允许UI/MAIN线程来操作。
- GC线程,java有垃圾回收机制,每个java程序都有一个专门负责垃圾回收的线程
- Binder1 就是我们的ApplicationThread,这个类实现了Ibinder接口,用于进程之间通信,具体来说,就是我们程序和AMS通信的工具
- Binder2 就是我们的ViewRoot.W对象,它也是实现了IBinder接口,就是用于我们的应用程序和 wms通信的工具。 wms就是WindowManagerServicer ,和ams差不多的概念,不过它是管理窗口的系统服务。