[TOC]
参考:https://www.jianshu.com/p/ab9b83a77af6,做一下记录和总结
Zygote进程的启动流程
Zygote进程又称受精卵进程,它由app_process启动,Zygote进程最大意义是作为一个Socket的Server端,接收着四面八方的进程创建请求,Android中所有的应用进程的创建都是一个应用进程通过Binder请求SystemServer进程,SystemServer进程发送socket消息给Zygote进程,统一由Zygote进程创建出来的。典型的C/S架构!!!。图中绿色标注为Binder通信方式,红色标注为Socket通信方式。
话说为什么Android系统采用这种架构呢,为什么所有进程的创建都是由Zygote来做呢?原因有如下几点
- Zygote进程在启动的时候会创建一个虚拟机实例,因此通过Zygote进程在父进程,创建的子进程都会继承这个虚拟机实例,App中的JAVA代码可以得到翻译执行。
- 进程与进程之间需要跨进程通信,由Zygote进程作为父进程还可以获得一个Binder线程池,这样进程之间就可以使用Binder进行跨进程通信了。
- 进程的“血液”可以理解成Message,启动四大组件靠的都是Looper消息机制,看过老罗的书,说由Zygote进程作为父进程,子进程可以获得一个消息循环。这句话我表示不理解,因为各个应用进程的Looper循环是自己在ActivityThread中创建的,SystemServer进程的消息循环也是自己创建的,那为什么说子进程可以继承Zygote进程的消息循环呢?这点我觉得不严谨,欢迎讨论。
所以这可以理解成Zygote进程作为所有应用进程的父进程的原因。
Java 源码启动分析
结合代码看:(ZygoteInit像是一个模版代码,其他的比如WebViewZygoteInit都是类似)
1 | frameworks/base/core/java/com/android/internal/os/ZygoteInit.java |
ZygoteInit的main方法大概就做了上面六件事情:
- 创建ZygoteServer,在Android O上把与Socket的操作都封装到了ZygoteServer类中;
- 解析app_main.cpp传来的参数。
- 创建一个Server端的Socket,作用是当Zygote进程将SystemServer进程启动后,就会在这个Socket上来等待ActivityManagerService请求,即请求创建我们自己APP应用程序进程;
- 预加载类和资源,包括颜色啊,R文件,drawable、类等;
- 启动system_server进程,这是上层framework的运行载体,ActivityManagerService就是运行在这个进程里面的;
- 开启一个循环,等待着接收ActivityManagerService的请求,随时待命,当接收到创建新进程的请求时立即唤醒并执行相应工作;
我觉得这段代码的主线是,ZygoteInit进程启动后,会注册一个Socket,在runSelectLoop方法中开启一个while死循环等待ActivityManagerService创建新进程的请求,其次,ZygoteInit启动了SystemServer进程,执行SystemServer的main方法。
Zygote进程的几个问题
1、ZygoteInit方法是怎么调用的?
Zygote进程被init初始化的时候,会调用app_main.cpp 的main函数,app_main.cpp是Zygote进程的主文件。
1 | /frameworks/base/cmds/app_process/app_main.cpp |
然后简单看一下Runtime的start方法:
1 | /* |
总结:Runtime.start 一共做了三件事情:
- 调用startVm开启虚拟机,
- 调用startReg注册JNI方法,
- 使用JNI把Zygote进程启动起来。
2、Socket是怎么注册的?Socket通信的原理是否还记得?
Socket原理:LocalSocket就是作为客户端建立于服务端的连接,发送数据。LocalServerSocket作为服务端使用,建立服务端的socket监听客户端请求。典型的C/S架构!
对于这种C/S架构,通常服务端会有个死循环不断响应客户端发送来的请求。
Socket是如何创建的呢?通过RegisterServerSocket,具体的呢:
1 | frameworks/base/core/java/com/android/internal/os/ZygoteServer.java |
根据从环境变量中取到的一个fd创建Socket,首先,Socket的监听方式为使用Linux系统调用select()函数监听Socket文件描述符,当该文件描述符上有数据时,自动触发中断,在中断处理函数中去读取文件描述符中的数据。那fd是什么时候进行数据输入的呢?通过init进程:内核启动完成之后会去读取init.rc文件,启动开机需要启动的进程。创建一个Socket,并写入部分信息:包括创建Socket fd及写入到环境变量,方便其他进程进行获取。
总结:init进程中add环境变量,Zygote进程中get环境变量。
3、SystemServer进程资源如何加载?
何为预加载?
android系统资源加载分两种方式,预加载和使用进程中加载。 预加载是指在zygote进程启动的时候就加载,这样系统只在zygote执行一次加载操作,所有APP用到该资源不需要再重新加载,减少资源加载时间,加快了应用启动速度,一般情况下,系统中App共享的资源会被列为预加载资源。预加载是什么原理?
预加载的原理很简单,就是在zygote进程启动后将资源读取出来,保存到Resources一个全局静态变量中,下次读取系统资源的时候优先从静态变量中查找。主要代码在zygoteInit.java类中方法preloadResources(),主要代码如下:系统哪些资源被预加载了?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
static void preload(BootTimingsTraceLog bootTimingsTraceLog) {
Log.d(TAG, "begin preload");
bootTimingsTraceLog.traceBegin("BeginIcuCachePinning");
beginIcuCachePinning();
bootTimingsTraceLog.traceEnd(); // BeginIcuCachePinning
bootTimingsTraceLog.traceBegin("PreloadClasses");
preloadClasses();
bootTimingsTraceLog.traceEnd(); // PreloadClasses
bootTimingsTraceLog.traceBegin("PreloadResources");
preloadResources();
bootTimingsTraceLog.traceEnd(); // PreloadResources
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
preloadOpenGL();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
preloadSharedLibraries();
preloadTextResources();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
WebViewFactory.prepareWebViewInZygote();
endIcuCachePinning();
warmUpJcaProviders();
Log.d(TAG, "end preload");
sPreloadComplete = true;
}
4、为什么Zygote进程与子进程通讯用Socket而不用Bindler
- TODO