一、概念
Zygote是 Android 中的第一个进程,负责孵化(fork)其它进程,而它自己由 Linux 内核启动的用户级进程 Init 创建。
二、作用
应用程序不能直接以本地进程的形态运行,必须在一个独立的虚拟机中运行,一些系统资源每个APP都会用到,如果每次都重新启动一个虚拟机,将严重拖慢应用程序的启动速度。
Linux 中 fork 出的子进程与父进程共享内存资源(子进程能访问父进程资源),只有当子进程改写内存时,操作系统才会为其分配一个新页面,并将老页面上的数据复制一份到新页面,这就是写时拷贝机制(Copy On Write,fork操作不会将进程中所有线程拷贝,只会拷贝当前线程)。
Zygote进程在初始化时会创建虚拟机并加载一些APP都会用到的系统资源,这样 fork 出的子进程能共享这些资源,接下来只需装载 apk 文件的字节码就可以运行应用程序了,可以大大缩短应用的启动时间。
三、启动过程
3.1 init.rc
入口:system/core/rootdir/init.rc
Zygote进程由 Linux 中的 init 进程解析 init.rc 文件(Linux命令脚本)以 Service 的方式启动,根据系统属性 ro.zygote 的具体值,加载不同的描述Zygote的rc脚本。
3.2 Native中
入口main函数:frameworks/base/cmds/app_process/app_main.cpp
最初名字叫“app_process”,这个名字是在Android.mk文件中指定的,但是在运行过程中Linux下的pctrl系统调用将名字换成了“zygote”。
int main(int argc, char* const argv[]) {
//创建虚拟机
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
//解析 init.rc 中的参数
while(i < argc) {...}
//app_process改名zygote
runtime.setArgv0(niceName.string(), true);
//执行ZygoteInit切换到Java世界
if(zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
}
}
- 创建虚拟机:
- 注册Android的JNI函数:
- 进入到Java世界:
3.3 Java中
入口main函数:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
//创建Server端的Socket用来通信
zygoteServer.registerServerSocket(socketName);
//预加载类和资源
preload(bootTimingsTraceLog);
//开启SystemService
forkSystemServer(abiList, socketName, zygoteServer);
//开启死循环监听Client端的消息
zygoteServer.runSelectLoop(abiList);
}
- 建立Socket通信:创建一个Server端的Socket用于Zygote与其它进程通信。(ANDROID_SOCKET_zygote,一旦有新进程需要执行系统就通过这个 socket 跟 zygote 通信)。
- 预加载类和资源:常用类文件、Resources资源、HALs资源、opengl、webview等一般都是只读的不会进行修改,而且是很多应用都可能会用到的,因此预先加载后,通过 Zygote 孵化出的进程共享虚拟机内存和框架层资源,这样大幅度提高应用程序的启动和运行速度。(类加载机制:加载子类都会先加载父类。被大量APP继承的基类就不会每次都要加载如AppCompatActivity)。
- 启动SystemService:
- 开启Loop循环:等待 Client 端消息去创建应用进程。
通讯机制 Socket
进程间通信是一对多模型,支持C/S(Client-Server)架构的只有 Socket 和 Binder,而采用 Socket 而不是 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机制,这个就是自找麻烦了。
- 性能问题:开进程是一个效率低的事情(处理慢),而Binder是一个传输效率高的机制(请求快),假如一下子开10个进程会导致请求都堵在那里,从200M飙到2G打乱系统内存分配。