Zygote
在Android系统中,zygote是一个native进程,是Android系统上所有应用进程的父进程,我们系统上app的进程都是由这个zygote分裂出来的。zygote则是由Linux系统用户空间的第一个进程——init进程,通过fork的方式创建的。
zygote进程做了两个重要的事情:
- 不断接收其它进程的信号,随时创建子进程(即app进程)
- 创建了嫡长子 —— system_server进程
zygote进程启动之后,首先创建了Java虚拟机,该虚拟机是Android系统启动后的第一个虚拟机,然后注册JNI调用,接着调用了Java层的ZygoteInit类的main函数,进入了Java的世界。接着,Java世界的ZygoteInit开始了zygote的工作,工作的步骤按顺序如下:
1) 建立一个Socket服务端,监听客户端的连接,用于IPC通信。
这里是为了响应创建子进程的请求,当收到请求时,zygote执行一系列操作,最后通过fork创建子进程,请求是在第4)步启动循环后处理的。举个例子,应用程序A通过 startActivity
启动了应用程序B的Activity,但此时B进程并未启动,那么,A将利用Binder机制向系统的ActivityManagerService
服务发送启动Activity的请求,而ActivityManagerService
服务驻留在system_server进程,因此A将把请求发往system_server进程,system_server进程进而新建一个LocalSocket
与zygote的LocalServerSocket
进行连接,向zygote进程发送启动子进程的请求,并带上参数"android.app.ActivityThread"
,zygote收到请求后通过fork的方式启动了子进程,并执行子进程的android.app.ActivityThread
类中的main()
函数,完成了应用程序B的进程的创建。
2)预加载类和资源
预加载类:系统有一个文件列表,保存着需要由zygote进行预加载的类的全路径,这些类是framework/base/tools/preload工具判断的加载时间超过1250微秒的类,zygote通过Class.forName()
的方式进行预加载。这个列表不小,每一行一个类,超过了1000行,这也是Android系统启动慢的原因之一。
zygote预加载类的一个好处是:预加载一次类后,在通过fork创建子进程时,只需要做一个复制即可,这样便加快了子进程的启动速度。
预加载资源:主要是加载framework-res.apk中的资源,在UI编程中常使用的com.android.R.XXX资源,是系统默认的资源,它们就是由zygote加载的。
3)通过fork的方式,启动system_server进程
system_server进程是zygote进程创建的第一个进程,也就是“嫡长子”,其中驻留着Android系统多个重要的服务,比如EntropyService、PowerManagerService、BatteryService、WindowManagerService、ActivityManagerService等。
zygote进程内部通过函数startSystemServer()
启动system_server进程,该函数采用了抛出异常后在异常捕获处继续执行的技巧,使得system_server进程跳过了zygote进程后续的步骤进入system_server的Java世界,这些步骤包括使得Zygote进入无限循环的runSelectLoopMode()
方法。
system_server的一个重要的特点是,它支持使用Binder进行进程间通信,它已经进入了Binder的世界,不用跟zygote进程一样使用Socket。
另一个特点是,system_server是和zygote共存亡的,只要system_server被杀死,zygote也会把自己杀掉,这就导致了系统的重启。
4)通过调用runSelectLoopMode()
方法,进入无限循环,等待客户端的连接请求,并处理请求。
这里可以发现,无论是zygote进程,还是system_server进程,或者是zygote分裂的应用子进程,他们的模式都是在进行必要的native初始化后,随即调用Java层某个类的main函数,从而进入Java的世界,主要逻辑都是在Java层完成。比如zygote进程的ZygoteInit.java类、system_server进程的SystemServer.java类、应用子进程的ActivityThread.java类。
关于fork
只在Unix系的系统中有这个函数,window系统是没有的。它是一个系统调用,调用它之后,可以创建一个与当前进程一模一样的进程,包括相同的进程上下文、堆栈地址、内存信息、PCB等。调用fork的进程称为父进程,fork将返回子进程的pid,而新的进程称为子进程,子进程将从fork()处开始执行,并且fork将返回0。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
pid_t fpid;
int r = rand();
printf("before fork\n");
fpid = fork();
printf("fpid:%d, rand: %d\n", fpid, r);
return 0;
}
以上代码的运行结果是:
before fork
fpid:18570,rand:1804289383
fpid:0,rand:1804289383
子进程没有输出“before fork”,说明子进程不是从头开始执行的,而是从fork()的调用处开始的,并且子进程的fork调用会返回0,不会再继续创建子进程了。因此,我们可以根据fork的返回值来确定当前进程是父进程还是子进程。
CSDN博客:https://blog.csdn.net/chz429/article/details/87514718
版权声明:本文为博主原创文章,转载请附上博文链接!
网友评论