Android世界的起源 — Zygote

如需转载请评论或简信,并注明出处,未经允许不得转载

目录

前言

默认情况下,每一个Android应用程序,都是一个独立的进程,并且每个进程都运行在一个独立的虚拟机中,这点我想很多人应该都知道。那么大家有没有想过,每一个应用进程和虚拟机,都是由谁创建的呢?创建的时机又是怎么样的呢?如何尽量减少创建一个新的进程的性能损耗呢?(即如何缩短应用的启动时间)

Zygote简介

我们今天要说的主角,就是Zygote,这个单词的中文翻译叫做“受精卵”,顾名思义,这就是Android应用程序的起点,它是 Android 系统中的第一个 Java 进程

Zygote的作用

  • 加载常用类、JNI函数、主题资源、共享库
  • 启动虚拟机
  • 初始化binder机制
  • 启动SystemServer(稍后具体分析)
  • 孵化应用进程

Zygote从何而来

那么这个Zygote又是由谁创建的呢?

Android系统启动后,首先引导加载程序会加载内核并启动init进程。然后,init进程会去读取一个配置文件init.rc,这个配置文件记录了需要启动的系统服务,例如android调试守护进程,USB守护进程等。当然这其中也包含创建Zygote进程的配置

有兴趣的可以通过adb shell查看设备中的文件,这里推荐使用模拟器(可以通过su命令获取root权限)

模拟器文件目录

下面粘贴了部分文件的内容

/vendor/default.prop

ro.zygote=zygote64_32

/init.rc

import /init.${ro.zygote}.rc
on zygote-start && property:ro.crypto.state=...
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start netd
    start zygote
    start zygote_secondary

/init.zygote64_32.rc

这里可以看出启动Zygote真正执行的其实是/system/bin/目录下的app_process64程序

#zygote代表进程名称
#/system/bin/app_process64代表程序的可执行路径
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    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

service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote_secondary stream 660 root system
    onrestart restart zygote
    writepid /dev/cpuset/foreground/tasks

所以总结一句话,Zygote就是Android应用程序的起点,它是由Linux内核的init进程创建的

Zygote的工作流程

提示:对看源码感到头疼的同学可以先看每段源码后面的结论,再根据自己的兴趣翻看对应源码

上面说到了真正执行的其实是/system/bin/目录下的app_process64程序(我的模拟器是64位的),它所对应的源代码是frameworks/base/cmds/app_process/app_main.cpp

app_main.cpp

#if defined(__LP64__)
static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist64";
static const char ZYGOTE_NICE_NAME[] = "zygote64";
#else
static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist32";
static const char ZYGOTE_NICE_NAME[] = "zygote";
#endif

int main(int argc, char* const argv[])
{

    //...
  
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;
  
    //解析app_process的参数
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }

    if (!niceName.isEmpty()) {
        runtime.setArgv0(niceName.string());
            //设置进程名称为“zygote”
        set_process_name(niceName.string());
    }

    //args中存储的是关于虚拟机的参数,主要有start-system-server,abi-list,socket-name=zygote
    if (zygote) {
        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.");
        return 10;
    }
}

这个方法主要做了下面几件事情:

  1. 解析app_process的参数
  2. 设置当前进程名由app_process改为zygote
  3. 调用是AndroidRuntime#start()

接下来程序就走到了frameworks\base\core\jni\AndroidRuntime.cpp

AndroidRuntime.cpp

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    static const String8 startSystemServer("start-system-server");

    //...
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    //启动虚拟机
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);

    //注册JNI函数
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
  
    //...
    char* slashClassName = toSlashClassName(className);
    //这里的startClass就是com.android.internal.os.ZygoteInit
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
    } else {
        //反射调用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);
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
        }
    }
    free(slashClassName);
}

这个方法主要做了下面几件事情:

  1. 启动虚拟机
  2. 注册JNI函数
  3. 调用ZygoteInit.main()方法

ZygoteInit是java方法,从这里开始就从native世界进入到了java世界

ZygoteInit.java

public static void main(String argv[]) {
            //创建ZygoteServer
        ZygoteServer zygoteServer = new ZygoteServer();

            //...

        final Runnable caller;
        try {
      
                        //...
            boolean startSystemServer = false;
            String socketName = "zygote";
            String abiList = null;
            boolean enableLazyPreload = false;
            //解析app_main.cpp传来的参数
            for (int i = 1; i < argv.length; i++) {
                if ("start-system-server".equals(argv[i])) {
                    startSystemServer = true;
                } else if ("--enable-lazy-preload".equals(argv[i])) {
                    enableLazyPreload = true;
                } else if (argv[i].startsWith(ABI_LIST_ARG)) {
                    abiList = argv[i].substring(ABI_LIST_ARG.length());
                } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
                    socketName = argv[i].substring(SOCKET_NAME_ARG.length());
                } else {
                    throw new RuntimeException("Unknown command line argument: " + argv[i]);
                }
            }

            if (abiList == null) {
                throw new RuntimeException("No ABI list supplied.");
            }

            //注册Zygote进程的socket通讯
            zygoteServer.registerServerSocketFromEnv(socketName);
          
            //...
          
            //加载进程的资源和类
                preload(bootTimingsTraceLog);
                
            //...
          
            if (startSystemServer) {
                //创建SystemServer进程,这是zygote进程的第一次分裂
                Runnable r = forkSystemServer(abiList, socketName, zygoteServer);

                if (r != null) {
                    r.run();
                    return;
                }
            }

            Log.i(TAG, "Accepting command socket connections");

                    //启动一个死循环监听来自Client端的消息
            caller = zygoteServer.runSelectLoop(abiList);
        } catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex);
            throw ex;
        } finally {
            //关闭SystemServer的Socket
            zygoteServer.closeServerSocket();
        }

        if (caller != null) {
            caller.run();
        }
    }

这个方法主要做了下面几件事情:

  1. 预加载类和资源,包括颜色,R文件、drawable类等
  2. 创建SystemServer进程
  3. 创建ZygoteServer,开启一个死循环,等待着接收ActivityManagerService的socket请求,即创建应用程序进程请求

SystemServer进程是一个非常重要的进程,我们来看看forkSystemServer()是如何创建SystemServer进程的

ZygoteInit.java

 private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
       
            //...
        //真正创建SystemServer进程
        int pid = Zygote.forkSystemServer(...);
             
        if (pid == 0) {
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }

            zygoteServer.closeServerSocket();
            //继续方法调用
            return handleSystemServerProcess(parsedArgs);
        }

        return null;
    }

fork() 函数是一次执行,两次返回。说的更严谨一点是 两个进程对用一个程序的两次执行。当 pid == 0 时,说明现在处于子进程,当 pid > 0 时,说明处于父进程

ZygoteInit.java

private static Runnable handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) {
        //....
        return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
}

ZygoteInit.java

public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
    //常规的一些初始化,比如日志输出、未捕获异常处理等
    RuntimeInit.commonInit();
    //主要用于初始化binder机制,并启动一个binder线程(用于SystemServer与其他进程通信)
    ZygoteInit.nativeZygoteInit();
    //调用SystemServer.java的入口函数
    return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}

所以forkSystemServer()主要做了下面几件事:

  1. 创建SystemServer进程
  2. 一些常规初始化
  3. 初始化Binder机制,用于SystemServer与其他进程的通信(如应用进程、ServiceManager进程等)
  4. 调用SystemServer.java的入口函数

SystemServer.mian()直接调用了SystemServer#run()方法

SystemServer.java

private void run() {
    //...
    //为主线程创建Looper
    Looper.prepareMainLooper();
    //加载共享库,即系统服务的native层代码
    System.loadLibrary("android_servers");
    //创建上下文   
    createSystemContext();
    //...
  
    //启动ActivityManagerService、SystemServiceManager、DisplayManagerService、
    //PackageManagerService、WindowManager等
    startBootstrapServices();
    startCoreServices();
    //开始准备HomeActivity的前奏工作,并通过Intent唤起桌面应用
    startOtherServices();
  
        //...
    // Loop forever.
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

下面放一张图概括一下上面的整个过程


android系统启动过程

总结

回到我们最初的问题

每一个应用进程和虚拟机,都是由谁创建的呢?

由Zygote进程创建

应用进程创建的时机又是怎么样的呢?

当Activity需要被启动的时候,SystemServer中负责管理进程的AMS利用Socket客户端向Zygote的Socket服务端发送创建新进程命令,Zygote收到命令后为应用创建进程,这是典型的C/S架构

如何尽量减少创建一个新的进程的性能损耗呢?

每个应用进程不仅有自己的数据资源和需要执行的代码,还需要用到Android框架中通用的资源和代码,由于这些资源和代码是大部分应用都需要调用到的,并且比较重量级,所以安卓把它们都放在Zygote进程的空间中,而应用进程又是继承于Zygote进程,所以当应用需要的时候就直接到里面去找就行了

本文主要讲了系统启动的流程,但是我们实际上并没有对Activity是如何被启动的做深入的分析,所以对Activity启动流程感兴趣的可以看Android应用进程的创建 — Activity的启动流程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容