我就问Zygote进程,你到底都干了啥

2,036 阅读7分钟

ZYGOTE

前言

OK,这是Android系统启动的第二篇文章。第二篇我们讲解一个我们一直都在用,但是却很少提起的进程---Zygote。 提到Zygote可能了解一些的小伙伴会说,它是分裂进程用的。没错它最大的作用的确是分裂进程,但是它除了分裂进程外还做了什么呢。 还是老规矩,让我们抱着几个问题来看文章。最后在结尾,再对问题进行思考回复。

  1. 你能大概描述一遍Zygote的启动流程吗
  2. 我们为什么可以执行java代码,和zygote有什么关系
  3. Zygote到底都做了哪些事情

另外,我最近也看了一些写底层的文章。要么就是言简意赅到只知道Zygote的作用,但是完全不知道如何实现的,就像是背作文一样。要么是全篇都是代码,又臭又长,让人完全没有看下去的动力。

不过作为一个读者,太长的我可能完全不想看,太短的又真的完全就是被课文一点都不明白原理。

因此,本篇文章,仍旧会引入一些代码,方便大家通过代码方便记忆。但是又会将代码进行精简,以免给大家造成过度疲劳。我们的目的都是希望用最少的时间,能掌握更多的知识。

读源码时:不要过于纠结细节,不要过于纠结细节,不要过于纠结细节!重要的事情说三遍!!!

OK,让我们进入正题瞅瞅Zygote到底是个什么东西。


1.C++还是Java

选这个当标题当然是有原因的。我们知道的是Android系统启动的时候,运行的是Linux内核,执行的是C++代码。这是一个很有趣的事情。

因为我们在写App的时候,AndroidStudio默认给我们创建的都是Activity.java,而选择的语言,要么是Java要么就是Kotlin

启动时候运行的是C++代码,应用层却可以使用Java代码,这到底是为什么呢?其实这就是Zygote的功劳之一

2.Native层

Init进程创建Zygote时,会调用app_main.cpp的main() 方法。此时它依旧运行的是C++代码。我们通过下面的代码,来看下它到底做了什么。

这里它做的最关键的一件事就是启动AndroidRuntime(Android运行时)。另外这里需要注意的是 start方法 中的 "com.android.internal.os.ZygoteInit" 这个类似于全类名。他到底是做什么的?别着急,大概2分钟后,你可能就会得到答案。

Zygote的新手村:app_main.cpp

int main(int argc, char* const argv[])
{
    //  创建Android运行时对象
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    // 代码省略...
    
    // 调用AppRuntime.start方法,
    // 而AppRuntime是AndroidRuntime的子类,并且没有重写start方法
    // 因此调用的是AndroidRuntime的start方法
    runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
}

精简后的代码告诉我们,这里一共就做了两件事,第一件创建AppRuntime,第二件调用start方法

不过,AppRuntimeAndroidRuntime的子类,他没有重写start方法,因此这里调用的是 AndroidRuntime的start() 方法。

奇迹的诞生地:AndroidRuntime:

这里我依旧只保留关键代码,源码关键注释也进行了保留。由下方代码看出这里做了三件改变命运的事情。

  1. startVM -- 启动Java虚拟机
  2. startReg -- 注册JNI
  3. 通过JNI调用Java方法,执行com.android.internal.os.ZygoteInit 的 main 方法
/*
 * Start the Android runtime. 
 */
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    /* start the virtual machine */
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
        return;
    }

    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    jmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");
    env->CallStaticVoidMethod(startClass, startMeth, strArray);
}

有了JVM,注册了JNI,我们就可以执行Java代码了。

这里我个人建议不要过于纠结细节,比如JVM是如何创建的,JNI是如何注册的。如果感兴趣的童鞋,可以下载源码,到app_main.cpp里进行查看。这里就不进行赘述了。否则代码量太大,适得其反。


3.Java层

命运的十字路口:ZygoteInit.java:

之所以说是命运的十字路口,因为Zygote会在这里创建SystemServer,但是二者却走向了截然不同的道路。

从这里就开始执行Java代码了,当然这些Java代码是运行在JVM中的。

让我们通过代码来看一下,到底都做了些什么。

class ZygoteInit{

    /**
     * This is the entry point for a Zygote process.  It creates the Zygote server, loads resources,
     * and handles other tasks related to preparing the process for forking into applications.
     * This process is started with a nice value of -20 (highest priority).  
     */
     // 上面是源码中的注释,小伙伴们可以自行翻译一下。
     // 创建了ZygoteServer,加载资源,并且介绍了这个进程的优先级是最高的-20.
    public static void main(String argv[]) {
        ZygoteServer zygoteServer = null;

        // 1. 预加载资源,常用的:resource,class,library等在此处进行加载
        preload(bootTimingsTraceLog);
            
        // 2. 创建ZygoteServer,实际是一个Socket用来进行跨进程间通信用的。
        zygoteServer = new ZygoteServer(isPrimaryZygote);

        // 3. fork出SystemServer进程,这个进程会创建AMS,ATMS,WMS,电池服务等一切只有你想不到没有它做不到的服务
        forkSystemServer(abiList, zygoteSocketName, zygoteServer);

        // 4.里面是一个while(true)循环,等待接收AMS创建进程的消息,类似于handler中的Looper.loop()
        zygoteServer.runSelectLoop(abiList);
    }
}

上面代码里的注释基本说明了ZygoteInit都干了啥。下面会再稍微总结下:

  1. 创建了ZygoteServer:这是一个Socket相关的服务,目的是进行跨进程通信。
  2. 预加载preload:预加载相关的资源。
  3. 创建SystemServer进程:通过forkSystemServer分裂出了两个进程,一个Zygote进程,一个SystemServer进程。而且由于是分裂的,所以新分裂出来的进程也拥有虚拟机,也能调用JNI,也拥有预加载的资源,也会执行后续的代码。
  4. 执行runSelectLoop():内部是一个while(true)循环,等待AMS创建新的进程的消息。(想想Looper.loop())

4. 戛然而止

没错就是这么突然,Zygote的故事到这就结束了。至于它是如何创建SystemServer,如何去创建App那就是后面的故事了。所以你还记得我的问题嘛?我替你总结一下Zygote到底做了什么:

  1. 创建虚拟机
  2. 注册JNI
  3. 回调Java方法ZygoteInit.java 的main方法,从这儿开始运行Java代码
  4. 创建ZygoteServer,内部包含Socket用于跨进程通信
  5. 预加载class,resource,library等相关资源
  6. fork 出了 SystemServer进程。他俩除了返回的pid不同,剩下一模一样。通过返回值不同来决定剩下的代码如何运行。这个留待后续进行讲解。
  7. 进入while(true)循环等待,等待AMS创建进程的消息(类似于Looper.loop()

5. 多说一句

这个系列才刚刚进行到第二章,我尽量让文章不是特别长的情况下,既有代码整理思路,又有总结方便记忆。代码是源码中摘抄的省略了很大一部分,只能保证执行顺序。有兴趣的小伙伴可以下载源码进行查看。
其实最近我看了很多文章和视频,最后却选择写文章来对知识进行总结。归根结底就是因为一句话,好记性不如烂笔头。要是真的想快速记住,真的需要亲自查看一下源码。翻源码的同时,也是在不知不觉中锻炼你阅读源码的能力。 万一以后让你学习一个新的库,你也知道大概该怎么看。

都已经到这儿了,就稍微厚个脸皮,希望各位看官,能够点个赞加个关注。毕竟一篇文章可能就要耗费几个小时的时间,上一篇文章就几个赞,感谢这几个小伙伴,让我有继续写下去的动力。 真心感谢你们。

另外,文章中有哪里出错,请及时指出。毕竟各位才是真正的大佬。希望各位Androider,可以一起取暖,度过这个互联网寒冬!加油各位!!!


6. Zygote功能图

zygote.png