Zygote
1. Zygote的作用
Android系统是基于Linux系统,所以当开机后,第一个启动的是Init进程,而后面所有的进程都是Init的子进程,Zygote就是Init进程通过解析init.rc文件之后,启动的一个进程。
主要作用
1. 启动SystemServer进程
SystemServer是一个用于启动手机内部各种服务的进程,我们常说的PMS、AMS等都是由SystemServer所启动。
2. 在系统运行过程中,即时地去孵化App进程
当我们每次点击App图标启动App时,Zygote就开始运作了。
关于孵化 Zygote进程创建别的进程的时候,用的不是创建,而是孵化这个词,如何理解孵化?
我们Android系统的程序,都是基于虚拟机所启动的,如果每次启动一个APP都要新启动一个虚拟机,则会很卡很慢,所以为了避免这种场景,Zygote进程在启动的时候,就会直接预加载虚拟机所需要的内存等资源,等后面创建应用需要用到的时候,直接共享使用,这样就避免了多次启动虚拟机的情况。
Zygote进程在创建进程的时候,最后会调到Linux内核自带的fork方法,来复制一个子进程,从而达到1所说的共享虚拟机内容的情况
Zygote进程的作用,就是生成别的进程,自己是不负责做事的
关于fork
fork在Zygote进程中是一个很重要的概念,这是Linux内核自带的一个方法,这个方法的作用就是复制一个子进程。那么,在理解这个复制的意思,简单来说,就是从成员变量,到内存空间,再到当前所执行的代码指令,都会生成一个副本后放到子进程中。
当进程A调用fork后,进程A和复制的子进程B,都会得到fork方向的return值,并且继续往下执行。返回值:若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1。
进程A在开辟一块内存空间之后,持有这个内存空间的引用的话,在fork之后,子进程B也会持有这个内存空间。
2. Zygote的启动流程
init进程读取init.rc文件后,根据文件里面的指令:
- 启动了虚拟机
- 注册了JNI(Java Native Interface,也就是我们常看见的那些Native方法)
- 启动了Zygote进程,具体启动方式就是通过反射,拿到ZygoteInit.java这个类,然后调用里面的main方法
init.rc
这段脚本要求init进程创建一个名为zygote的进程,该进程要执行的程序是"/system/bin/app_process"。并且为zygote进程创建一个socket资源 (用于进程间通信,ActivityManagerService就是通过该socket请求 zygote进程fork一个应用程序进程)。
c
// system\core\rootdir\init.zygote32.rc
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
Zygote要执行的程序便是system/bin/app_process,它的源代码在frameworks/base/cmds/app_process/app_main.cpp
- App_main::main:
c
int main(int argc, char* const argv[])
{
...
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) {//是否有--zygote参数。这个是启动zygote进程的时候的参数
zygote = true;
// 进程名称,设置为zygote
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {//是否有--start-system-server
startSystemServer = true;
....
if (zygote) {
// 如果是zygote进程,则启动ZygoteInit。
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
}
}
- AndroidRuntime::start
c
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
...
JNIEnv* env;
//重点方法:创建VM虚拟机,参数是指针,可以用于获取返回的值,可以使用env来和Java层来做交互
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);
//重点方法:给虚拟机注册一些JNI函数(系统so库、用户自定义so库 、加载函数等。)
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
//找到类的main方法,并调用。如果是zygote的话,这里就会启动ZygoteInit类的main方法
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
//调用main方法。这里通过JNI调用Java方法之后,Zygote(Native层)就进入了Java的世界,从而开启了Android中Java的世界。
env->CallStaticVoidMethod(startClass, startMeth, strArray);
}
}
总结
c
App_main.main
AndroidRuntime.start
startVm//创建虚拟机
startReg//注册JNI函数
ZygoteInit.main//这里就进入到了Java层了
registerZygoteSocket//建立IPC的通讯机制
preload//预加载类和资源
startSystemServer//启动system_server
runSelectLoop//等待进程创建的请求
Zygote进程的启动过程中,除了会创建一个Dalvik虚拟机 实例外,还会将Java运行时库加载到进程中。
Zygote进程初始化时启动虚拟,并加载一些系统资源。这样Zygote fork出子进程之后,子进程也会继承能正常工作的虚拟机和各种系统资源,剩下的只需要装载APK文件和字节码就可以运行程序。
Java应用程序不能以本地进程的形态运行,必须在一个独立的虚拟机中运行。如果每次都重新启动虚拟机,肯定就会拖慢应用程序的启动速度。
- 注意:APK应用程序进程被Zygote进程孵化出来以后,不仅会获得Dalvik虚拟机实例拷贝,还会与Zygote一起共享Java运行时库。
3. Zygote的工作原理
3.1 工作入口:ZygoteInit
- ZygoteInit.java
C
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
//....
// 注册socket
zygoteServer.registerServerSocketFromEnv(socketName);
// 预加载资源
preload(bootTimingsTraceLog);
// 启动 systemServer
Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
// loops forever in the zygote
caller = zygoteServer.runSelectLoop(abiList);
}
3.2 启动SystemServer
如果app_process的调用参数中带有"---start-system-server",那么此时会通过forkSystemServer来启动。这里稍后再看,先考虑一个问题:
Zygote在前期主要担任启动系统服务的工作,后期则又担当"孵化进程"的重任,但是Zygote只在 init.rc中被启动一次,它如何协调好这两项工作呢?通过forkSystemserver这个方法名,我们可以明显看出这是启动了一个新的进程来承载系统服务,而我们推断app_process所在进程会继续执行,从而转变为Zygote的"孵化器"守护进程。
forkSystemServer
还记得Zygote启动时,是使用fork+execve方式,而此处使用fork+handle(即直接继承资源,未使用 execve进行资源覆盖),简单表示如下:
接着来看,handleSystemServerProcess方法里最后调用的是RuntimeInit.zygoteInit方法:
c
void zygoteInit(String[] argv, ..,){
commonInit();
nativeZygoteInit();
applicationInit(argv, ...)
}
这个方法比较短,核心就上面三行,我们来介绍一下:
-
commonInit:通用部分的初始化,包括设置默认的uncaught exception handler;TimeZone,LogManager,AndroidConfig, HTTP User-Agent等模块的初始化。
-
nativeZygoteInit:本地初始化函数,也是zygoteInit中的重点
-
applicationInit:它是SystemServer的"起点",而参数argv里会带有startClass和startArgs。这两个变量是在ZygoteInit.forkSystemServer方法定义并传入的参数。实际上最终将调用SystemServer的main方法
经过这个zygoteInit方法之后,程序现在有两个分支,其一是nativeZygoteInit主导的native系统服务启动;另一个则是applicationInit负责的Java层系统服务启动。
Native系统服务启动
c
virtual void onZygoteInit(){
sp<ProcessState> proc = ProcessState::self();
proc->startThreadPool();
}
这段代码是Binder机制中的重要组成部分,其中startTheardPool将开启Binder线程池,以保证其它进程可以正确访问的Zygote所提供的服务。Zygote通过JNI和回调的方式非常巧妙的把Native层和Java层、SystemServer和app process关联起来了。
Java层系统服务启动
System Server在启动前首先需要做很多初始化设置,包括将VM的版本记录到系统变量中,设置线程优先级,设置堆的使用率。
我们知道,Android的系统服务也被分为两类,其中,Native层通过 System.loadLibrary("android_servers") 加载,而java层又继续细分如下:
-
startBootstrapServices(): "引导程序"服务,代表系统服务中最核心的部分,如 ActivityManagerService, Power Manager, Display Manger, PackageManger等
-
startCoreServices():次核心,包括LED和背光管理器,电池电量管理器,应用程序使用情况(Usage Status)管理器等
-
startOtherServices():优先级较低,但是Service数最多,如AccountManagerService, VibratorService, MountService, NetworkManagementService, WindowMangerService, AudioService 等。这些服务一起构建起Android System Server这座"参天大厦",为其它进程、应用程序的正常运行奠定了基础。
最后,SystemServer通过Looper.loop() 进入死循环,并依托onZygoteInit中启动的Binder服务接受和处理外界的请求。
c
private void run(){
Looper.prepareMainLooper();
System.loadLibrary("android_servers:);
createSystemContext();
startBootstrapServices();
startCoreServices();
startOtherServices();
Looper.loop()
}
3.3 孵化应用进程
runSelectLoop从方法名可以猜到,这应该是一个死循环,除非Zygote退出或出现异常才会跳出循环。
我们看到runSelectLoop的主体的确是一个while死循环,它将作为zygote的守护体存在。
mServerSocket.getFileDescriptor() 获取到的是前面通过registerServerSocketFromEnv创建的Server Socket的文件描述符。但它没有回到fds中,这同时也说明了zygote中不止有一个文件描述符来与zygotę通讯,通过后面的for循环,找出当前可读的fd 。
i == 0 时
fds中,添加的第一个元素为Zygote的Server Socket,所以此时表示有新的连接请求,这和网络连接中的 Socket概念是一致的。
我们通过acceptCommandPeer接受一个新的客户端连接,产生一个新的ZygoteConnection ,然后更新peers 和fds这样值班表中对象的index就是一致。(这就是为啥前面 peers.add(null))
i > 0 时
说明已经建立Socket连接中有来自客户端的数据需要处理,具体的工作就是 processOneCommand
这边同样是典型的使用fork + handle 模式来处理fork进程。不用细看也知道做了两件事:
创建承载应用程序的新进程值得注意的是,这边没有直接用fork方法来启动新进程,而调用了 forkAndSpecialize方法。Specialize意为 "特殊化",其实就是在fork的同时也把它转变为Android应用程序。
AMS向Zygote发起创建进程请求时传入的参数,其中有最重要的参数之一是className (android.app.ActivityThread), handleChildProc会把这个类加载到内存中,然后执行它的main函数。换句话说,我们熟悉的Android应用程序的"主线程"就此启动了
父进程的扫尾工作handleParentProc包括将子进程加入进程组,正确关闭文件,调用方返回结果值等
c
Runnable runSelectLoop(String abiList) {
//创建两个数组
//数组socketFDs里面存放的是一些fd,用来做socket通信的
//数组peers里面存放的是ZygoteConnection
ArrayList<FileDescriptor> socketFDs = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
//第0位添加一个从ZygoteSocket获取的fd
socketFDs.add(mZygoteSocket.getFileDescriptor());
peers.add(null);
while (true) {
//这前面的一大段逻辑都是处理UsapPool相关的
//并且会将socketFDs进行一些处理,构造出一个structs poll的结构体,然后通过USAP连接池进行处理
//根据Zygote的USAP池的状态有不同的Zygote(可以是常规Zygote,WebView Zygote或AppZygote)。
...
try {
//等待事件socket的fd事件
Os.poll(pollFDs, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
boolean usapPoolFDRead = false;
while (--pollIndex >= 0) {
if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
continue;
}
if (pollIndex == 0) {
// ZygoteSocket收到请求,通过acceptCommandPeer创建一个ZygoteConnection
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
socketFDs.add(newPeer.getFileDescriptor());
} else if (pollIndex < usapPoolEventFDIndex) {
// Session socket accepted from the Zygote server socket
try {
ZygoteConnection connection = peers.get(pollIndex);
//处理socket消息
final Runnable command = connection.processOneCommand(this);
//在Child端
if (mIsForkChild) {
if (command == null) {
throw new IllegalStateException("command == null");
}
return command;
} else {
//在Server端
if (command != null) {
throw new IllegalStateException("command != null");
}
// We don't know whether the remote side of the socket was closed or
// not until we attempt to read from it from processOneCommand. This
// shows up as a regular POLLIN event in our regular processing loop.
if (connection.isClosedByPeer()) {
connection.closeSocket();
peers.remove(pollIndex);
socketFDs.remove(pollIndex);
}
}
} catch (Exception e) {
if (!mIsForkChild) {
//在server端,因此这里发生的任何异常之前都是
//在处理命令或从读写socket。 对此做一个提醒以便
//确切知道失败的原因。
// We're in the server so any exception here is one that has taken place
// pre-fork while processing commands or reading / writing from the
// control socket. Make a loud noise about any such exceptions so that
// we know exactly what failed and why.
Slog.e(TAG, "Exception executing zygote command: ", e);
//关闭socket避免超时
ZygoteConnection conn = peers.remove(pollIndex);
conn.closeSocket();
socketFDs.remove(pollIndex);
} else {
//在child端,异常发生在ActivityThread.main之前
Log.e(TAG, "Caught post-fork exception in child process.", e);
throw e;
}
} finally {
//重置child的标志,在Runnable之后不在使用
mIsForkChild = false;
}
} else {
// USAP池事件FD或USAP报告管道。
//如果这是USAP池事件FD,则有效负载将是已删除的USAP的数量。
//如果这是USAP报告管道FD,则有效负载将是USAP的PID
//这只是专门的。
long messagePayload = -1;
try {
byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES];
int readBytes = Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);
if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) {
DataInputStream inputStream =
new DataInputStream(new ByteArrayInputStream(buffer));
messagePayload = inputStream.readLong();
} else {
Log.e(TAG, "Incomplete read from USAP management FD of size "
+ readBytes);
continue;
}
} catch (Exception ex) {
if (pollIndex == usapPoolEventFDIndex) {
Log.e(TAG, "Failed to read from USAP pool event FD: "
+ ex.getMessage());
} else {
Log.e(TAG, "Failed to read from USAP reporting pipe: "
+ ex.getMessage());
}
continue;
}
if (pollIndex > usapPoolEventFDIndex) {
Zygote.removeUsapTableEntry((int) messagePayload);
}
usapPoolFDRead = true;
}
}
//检查USAP池是否需要重新填充
if (usapPoolFDRead) {
int[] sessionSocketRawFDs =
socketFDs.subList(1, socketFDs.size())
.stream()
.mapToInt(fd -> fd.getInt$())
.toArray();
final Runnable command = fillUsapPool(sessionSocketRawFDs);
if (command != null) {
return command;
}
}
}
}
4. 总结
4.1 孵化应用进程为什么不给SystemServer来做,而专门设计一个Zygote?
我们知道,应用在启动的时候需要做很多准备工作,包括启动虚拟机,加载各类系统资源等等,这些都是非常耗时的,如果能在zygote里就给这些必要的初始化工作做好,子进程在fork的时候就能直接共享,那么这样的话效率就会非常高。这个就是zygote存在的价值,这一点呢SystemServer是替代不了的,主要是因为SystemServer里跑了一堆系统服务,这些是不能继承到应用进程的。而且我们应用进程在启动的时候,内存空间除了必要的资源外,最好是干干净净的,不要继承一堆乱七八糟的东西。所以呢,不如给SystemServer和应用进程里都要用到的资源抽出来单独放在一个进程里,也就是这的zygote进程,然后zygote进程再分别孵化出SystemServer进程和应用进程。孵化出来之后,SystemServer进程和应用进程就可以各干各的事了。
4.2 Zygote的IPC通信机制为什么不采用binder?
第一个原因,我们可以设想一下采用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机制,这个就是自找麻烦了。