Error[8]: Undefined offset: 13, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

概述     Android9Zygote进程启动源码分析指南二前言  各位老司机们,现在闲下来终于有时间接着续写Android9Zygote进程启动源码分析指南二了,在前面的篇章AndroidPZygote进程启动源码分析指南一中,我们已经讲解了zygote启动的前面阶段主要是为了孵化Android世界做      AndroID 9 Zygote进程启动源码分析指南二

前言

  各位老司机们,现在闲下来终于有时间接着续写AndroID 9 Zygote进程启动源码分析指南二了,在前面的篇章Android P Zygote进程启动源码分析指南一中,我们已经讲解了zygote启动的前面阶段主要是为了孵化AndroID世界做的前期准备工作,大概的内容如下所示:

Zygote进程启动流程整体概括Zygote 进程从何而来zygote创建参数解析创建虚拟机注册JNI函数

都说zygote开创了AndroID的世界,创造了五彩缤纷的AndroID生态,孵化了各式的上层App。那么本篇将会带领大伙看看,我们的zygote是怎么做到的!要的罗,让我们开干。

注意:本文演示的代码是AndroID P高通msm8953平台源码。其中涉及的源码路径如下:

frameworks/base/include/androID_runtime/AndroIDRuntime.hframeworks/base/core/jni/AndroIDRuntime.cppframeworks/base//core/java/com/androID/internal/os/ZygoteInit.java


一. 通过JNI反射启动ZygoteInit的main方法

  在前面zygote启动阶段已经创建好了虚拟机,注册好了系统JNI函数,一切就绪只欠东风了!在接下来通过JNI的反射调用Java的类了。关于JNI的语法如果大伙有不熟悉的可以参见系列篇章JNI/NDK入门一锅端。


1.1 JNI反射启动ZygoteInit的main

  还是熟悉的味道,还是原来的配方,经过一顿猛如虎的 *** 作最终调用CallStaticVoIDMethod反射从而调用ZygoteInit的main函数,

char* AndroIDRuntime::toSlashClassname(const char* classname){    char* result = strdup(classname);    for (char* cp = result; *cp != '	private static final String SOCKET_name_ARG = "--socket-name=";	public static voID main(String argv[]) {			....            String socketname = "zygote";            String abiList = null;            boolean enableLazyPreload = false;            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.");            }            zygoteServer.registerServerSocketFromEnv(socketname);//注册zygote进程和AMS交互的通道            ......            preload(boottimingsTraceLog);//预加载类和资源			gcAndFinalize();//GC *** 作            if (startSystemServer) {                Runnable r = forkSystemServer(abiList, socketname, zygoteServer);//启动system_server                // {@code r == null} in the parent (zygote) process, and {@code r != null} in the                // child (system_server) process.                if (r != null) {                    r.run();                    return;                }            }            Log.i(TAG, "Accepting command socket connections");            // The select loop returns early in the child process after a fork and            // loops forever in the zygote.            caller = zygoteServer.runSelectLoop(abiList);//进入循环模式	......}'; cp++) {        if (*cp == '.') {            *cp = '/';        }    }    return result;}voID AndroIDRuntime::start(const char* classname, const Vector<String8>& options, bool zygote){	......    /*     * We want to call main() with a String array with arguments in it.     * At present we have two arguments, the class name and an option string.     * Create an array to hold them.     */    jclass stringClass;//申明一些jni变量,没有什么好说的    jobjectArray strArray;    Jstring classnameStr;    stringClass = env->FindClass("java/lang/String");    assert(stringClass != NulL);    strArray = env->NewObjectArray(options.size() + 1, stringClass, NulL);    assert(strArray != NulL);    classnameStr = env->NewStringUTF(classname);    assert(classnameStr != NulL);    env->SetobjectArrayElement(strArray, 0, classnameStr);    for (size_t i = 0; i < options.size(); ++i) {        Jstring optionsstr = env->NewStringUTF(options.itemAt(i).string());        assert(optionsstr != NulL);        env->SetobjectArrayElement(strArray, i + 1, optionsstr);    }    /*     * Start VM.  This thread becomes the main thread of the VM, and will     * not return until the VM exits.     */     /*     *将字符中的.转换为/,我们前面传递过来的字符串是com.androID.internal.os.ZygoteInit,     *通过这里转换为com/androID/internal/os/ZygoteInit,不要问我为啥要这样这个是JNI的规范     */    char* slashClassname = toSlashClassname(classname != NulL ? classname : "");    jclass startClass = env->FindClass(slashClassname);    if (startClass == NulL) {        ALOGE("JavaVM unable to locate class '%s'\n", slashClassname);        /* keep going */    } else {        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 {            env->CallStaticVoIDMethod(startClass, startMeth, strArray);//调用main方法#if 0            if (env->ExceptionCheck())                threadExitUncaughtException(env);#endif        }    }    free(slashClassname);    ALOGD("Shutting down VM\n");    if (mJavaVM->DetachCurrentThread() != JNI_OK)//退出当前线程        ALOGW("Warning: unable to detach main thread\n");    if (mJavaVM->DestroyJavaVM() != 0)//等待所有线程结束关闭虚拟机        ALOGW("Warning: VM dID not shut down cleanly\n");	......}


二. 登陆Java世界

  兜兜转转,调用CallStaticVoIDMethod终于登陆Java的世界了,Java世界真香,让我们畅游一番。


2.1 ZygoteInit.main

  这里不过多详细解释,在main方法中主要做了如下几件事情:

解析参数调用zygoteServer.registerServerSocketFromEnv创建zygote通信的服务端调用forkSystemServer启动system_serverzygoteServer.runSelectLoop进入循环模式
    /**     * Registers a server socket for zygote command connections. This locates the server socket     * file descriptor through an ANDROID_SOCKET_ environment variable.     *     * @throws RuntimeException when open fails     */    voID registerServerSocketFromEnv(String socketname) {        if (mServerSocket == null) {            int fileDesc;            final String fullSocketname = ANDROID_SOCKET_PREFIX + socketname;            try {                String env = System.getenv(fullSocketname);                fileDesc = Integer.parseInt(env);            } catch (RuntimeException ex) {                throw new RuntimeException(fullSocketname + " unset or invalID", ex);            }            try {                fileDescriptor fd = new fileDescriptor();                fd.setInt$(fileDesc);//设置文件描述符                mServerSocket = new LocalServerSocket(fd);//创建socket本地服务端                mCloseSocketFd = true;            } catch (IOException ex) {                throw new RuntimeException(                        "Error binding to local socket '" + fileDesc + "'", ex);            }        }    }

2.2 zygoteServer.registerServerSocketFromEnv

  该代码的路径是frameworks/base/core/java/com/androID/internal/os/ZygoteServer.java,这里主要是通过解析zygote启动传入的变量值得到 zygote 文件描述符,根据该文件描述符创建 socket,用来和 ActivityManagerService 通信。AMS 通过 Process.start 来创建新的进程,而Process.start 会先通过 socket 连接到 zygote 进程,并最终由 zygote 完成进程创建。这里传入的-为-socket-name=zygote.。

//定义在frameworks/base/core/java/androID/os/Process.java    public static final ProcessstartResult start(final String processClass,                                  final String nicename,                                  int uID, int gID, int[] gIDs,                                  int runtimeFlags, int mountExternal,                                  int targetSdkVersion,                                  String seInfo,                                  String abi,                                  String instructionSet,                                  String appDataDir,                                  String invokeWith,                                  String[] zygoteArgs) {        return zygoteProcess.start(processClass, nicename, uID, gID, gIDs,                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,                    abi, instructionSet, appDataDir, invokeWith, zygoteArgs);    }	//定义在frameworks/base/core/java/androID/os/ZygoteProcess.java    public final Process.ProcessstartResult start(final String processClass,                                                  final String nicename,                                                  int uID, int gID, int[] gIDs,                                                  int runtimeFlags, int mountExternal,                                                  int targetSdkVersion,                                                  String seInfo,                                                  String abi,                                                  String instructionSet,                                                  String appDataDir,                                                  String invokeWith,                                                  String[] zygoteArgs) {        try {            return startViaZygote(processClass, nicename, uID, gID, gIDs,                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,                    abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,                    zygoteArgs);        } catch (ZygoteStartFailedEx ex) {            Log.e(LOG_TAG,                    "Starting VM process through Zygote Failed");            throw new RuntimeException(                    "Starting VM process through Zygote Failed", ex);        }    }    private Process.ProcessstartResult startViaZygote(final String processClass,                                                      final String nicename,                                                      final int uID, final int gID,                                                      final int[] gIDs,                                                      int runtimeFlags, int mountExternal,                                                      int targetSdkVersion,                                                      String seInfo,                                                      String abi,                                                      String instructionSet,                                                      String appDataDir,                                                      String invokeWith,                                                      boolean startChildZygote,                                                      String[] extraArgs)                                                      throws ZygoteStartFailedEx {	......	        synchronized(mlock) {            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);        }	......}           private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {        Preconditions.checkState(Thread.holdsLock(mlock), "ZygoteProcess lock not held");        if (primaryZygoteState == null || primaryZygoteState.isClosed()) {            try {                primaryZygoteState = ZygoteState.connect(mSocket);            } catch (IOException ioe) {                throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);            }            maybeSetAPIBlackListExemptions(primaryZygoteState, false);            maybeSetHIDdenAPIAccessLogSampleRate(primaryZygoteState);        }        if (primaryZygoteState.matches(abi)) {            return primaryZygoteState;        }        // The primary zygote dIDn't match. Try the secondary.        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {            try {                secondaryZygoteState = ZygoteState.connect(mSecondarySocket);            } catch (IOException ioe) {                throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);            }            maybeSetAPIBlackListExemptions(secondaryZygoteState, false);            maybeSetHIDdenAPIAccessLogSampleRate(secondaryZygoteState);        }        if (secondaryZygoteState.matches(abi)) {            return secondaryZygoteState;        }        throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);    }                                               

  LocalSocket是AndroID的妈咪谷歌为我们带来了,比Java本身的socket效率要高,没有经过协议栈,是AndroID自己实现的类似共享内存一样的东东,在传输大量数据的时候就需要用到,关于LocalSocket的介绍和使用大伙可以参考篇章Android Framework层和Native层通过LocalSocket实现通信。

  我们知道ActivityManagerService 通过 Process.start 来创建新的进程,而 Process.start 会先通过socket 连接到 zygote 进程,并最终由 zygote 完成进程创建。如下是App进程创建请求Zygote创建新的进程。

//预加载位于/system/etc/preloaded-classes文件中的类    preloadClasses();    //预加载资源,包含drawable和color资源    preloadResources();    //预加载OpenGL    preloadOpenGL();    //通过System.loadlibrary()方法,    //预加载"androID","compiler_rt","jnigraphics"这3个共享库    preloadSharedlibrarIEs();    //预加载 文本连接符资源    preloadTextResources();    //仅用于zygote进程,用于内存共享的进程    WebVIEwFactory.prepareWebVIEwInZygote();

  我不知道大伙读到这里是否有一个疑问,为啥AMS和Zygote通信使用的是socket而不是AndroID的杜门绝技Binder,这个当初学习源码的时候也是我思考的,来大伙思考思考为啥AndroID的设计者是这么设计的呢,我认为主要有如下几个方面的考虑:

zygote比service manager先启动,从这个意义触发,zygote没有service manager可以注册binder,所以没有办法binderzygote进程和service manager进程都是由init进程启动的,那怕先启动service manager,也不能保证zygote起来的时候service manager启动好了,这样就需要额外的同步同时这个socket的所有者是root,用户组是system,只有系统权限的用户才能读写,这又多了一个安全保障(这里的socket是unix域的socket,而不是internet域的socket)最最主要的是zygote是通过fork生成进程的,而多线程是不允许fork的,可能造成死锁,同时Binder又是多线程,为了避免这些麻烦所以干脆使用socket
2.3 preload预加载

  预加载类和资源,androID Java 进程都是由 zygote 进程 fork,zygote 通过预加载类和资源可以加快子进程的执行速度和优化内存。因为预加载的类和资源较多,在开机优化过程中也需要重点关注 preload 的耗时。

[LandroID.accounts.Account;[LandroID.animation.Animator;[LandroID.animation.Keyframe$floatKeyframe;[LandroID.animation.Keyframe$IntKeyframe;[LandroID.animation.Keyframe$ObjectKeyframe;[LandroID.animation.Keyframe;[LandroID.animation.PropertyValuesHolder;[LandroID.app.LoaderManagerImpl;[LandroID.content.ContentProvIDerResult;[LandroID.content.ContentValues;[LandroID.content.Intent;[LandroID.content.UndoOwner;[LandroID.content.pm.ActivityInfo;[LandroID.content.pm.ConfigurationInfo;[LandroID.content.pm.FeatureGroupInfo;[LandroID.content.pm.FeatureInfo;[LandroID.content.pm.InstrumentationInfo;[LandroID.content.pm.PathPermission;
preloadClasses()预加载的文件路径为/system/etc/preloaded-classes,预加载比较多有大概4000多个类,这里对于类的加载主要通过Class.forname()方法来进行的。
06-04 15:39:01.041  2901  2901 D Zygote  : begin preload06-04 15:39:01.041  2901  2901 I Zygote  : Installing ICU cache reference pinning...06-04 15:39:01.041  2901  2901 I Zygote  : preloading ICU data...06-04 15:39:01.061  2901  2901 I Zygote  : preloading classes...06-04 15:39:01.065  2901  2901 W Zygote  : Class not found for preloading: [LandroID.vIEw.display$colortransform;06-04 15:39:01.150  2901  2901 E Typeface: Error mapPing Font file /system/Fonts/DroIDSansFallback.ttf06-04 15:39:01.291  2901  2901 I art     : Thread[1,tID=2901,Native,Thread*=0x7f77696a00,peer=0x12c060d0,"main"] recursive attempt to load library "/system/lib64/libmedia_jni.so"06-04 15:39:01.291  2901  2901 D MtpDeviceJNI: register_androID_mtp_MtpDevice06-04 15:39:01.292  2901  2901 I art     : Thread[1,tID=2901,Native,Thread*=0x7f77696a00,peer=0x12c060d0,"main"] recursive attempt to load library "/system/lib64/libmedia_jni.so"06-04 15:39:01.292  2901  2901 I art     : Thread[1,tID=2901,Native,Thread*=0x7f77696a00,peer=0x12c060d0,"main"] recursive attempt to load library "/system/lib64/libmedia_jni.so"06-04 15:39:01.316  2901  2901 D         : Environment::getoverlayDir() = /system/vendor/Default/system/vendor/overlay06-04 15:39:01.352  2901  2901 W Zygote  : Class not found for preloading: androID.vIEw.display$colortransform06-04 15:39:01.352  2901  2901 W Zygote  : Class not found for preloading: androID.vIEw.display$colortransform6-04 15:39:01.449  2901  2901 I System  : Loaded time zone names for "" in 32ms (30ms in ICU)06-04 15:39:01.469  2901  2901 I System  : Loaded time zone names for "en_US" in 19ms (16ms in ICU)06-04 15:39:01.491  2901  2901 I System  : Loaded time zone names for "zh_CN" in 22ms (20ms in ICU)06-04 15:39:01.518  2901  2901 I Zygote  : ...preloaded 4158 classes in 457ms.06-04 15:39:01.518  2901  2901 I art     : vmruntime.preloadDexCaches starting06-04 15:39:01.572  1374  1419 E slim_daemon: [NDK] bindNDKSensors: Sensor server is unavailable.06-04 15:39:01.664  2901  2901 I art     : vmruntime.preloadDexCaches strings total=292199 before=40345 after=4034506-04 15:39:01.665  2901  2901 I art     : vmruntime.preloadDexCaches types total=24111 before=8040 after=807506-04 15:39:01.665  2901  2901 I art     : vmruntime.preloadDexCaches fIElds total=115222 before=41429 after=4166006-04 15:39:01.665  2901  2901 I art     : vmruntime.preloadDexCaches methods total=201879 before=83038 after=8378206-04 15:39:01.665  2901  2901 I art     : vmruntime.preloadDexCaches finished06-04 15:39:01.666  2901  2901 I Zygote  : preloading resources...06-04 15:39:01.682  2901  2901 W Resources: Preloaded drawable resource #0x108025d (androID:drawable/dialog_background_material) that varIEs with configuration!!06-04 15:39:01.683  2901  2901 W Resources: Preloaded drawable resource #0x1080299 (androID:drawable/editBox_dropdown_background_dark) that varIEs with configuration!!06-04 15:39:01.686  2901  2901 W Resources: Preloaded color resource #0x10600e2 (androID:color/material_grey_800) that varIEs with configuration!!06-04 15:39:01.686  2901  2901 W Resources: Preloaded drawable resource #0x10802ca (androID:drawable/floating_popup_background_dark) that varIEs with configuration!!06-04 15:39:01.694  2901  2901 W Resources: Preloaded drawable resource #0x108030c (androID:drawable/ic_clear_Disabled) that varIEs with configuration!!06-04 15:39:01.694  2901  2901 W Resources: Preloaded drawable resource #0x1080311 (androID:drawable/ic_clear_normal) that varIEs with configuration!!06-04 15:39:01.698  2901  2901 W Resources: Preloaded drawable resource #0x108035a (androID:drawable/ic_go) that varIEs with configuration!!06-04 15:39:01.704  2901  2901 W Resources: Preloaded drawable resource #0x1080038 (androID:drawable/ic_menu_close_clear_cancel) that varIEs with configuration!!06-04 15:39:01.707  2901  2901 W Resources: Preloaded drawable resource #0x1080045 (androID:drawable/ic_menu_more) that varIEs with configuration!!06-04 15:39:01.714  2901  2901 W Resources: Preloaded drawable resource #0x10804f1 (androID:drawable/menu_background_fill_parent_wIDth) that varIEs with configuration!!06-04 15:39:01.744  2901  2901 W Resources: Preloaded drawable resource #0x1080787 (androID:drawable/text_edit_paste_window) that varIEs with configuration!!06-04 15:39:01.748  2901  2901 W Resources: Preloaded drawable resource #0x1080096 (androID:drawable/toast_frame) that varIEs with configuration!!06-04 15:39:01.748  2901  2901 I Zygote  : ...preloaded 114 resources in 82ms.06-04 15:39:01.752  2901  2901 W Resources: Preloaded color resource #0x106010b (androID:color/background_cache_hint_selector_material_dark) that varIEs with configuration!!06-04 15:39:01.753  2901  2901 W Resources: Preloaded color resource #0x1060110 (androID:color/btn_default_material_dark) that varIEs with configuration!!06-04 15:39:01.755  2901  2901 I Zygote  : ...preloaded 41 resources in 7ms.06-04 15:39:01.757  2901  2901 D libEGL  : loaded /vendor/lib64/egl/libEGL_adreno.so06-04 15:39:01.773  2901  2901 D libEGL  : loaded /vendor/lib64/egl/libGLESv1_CM_adreno.so06-04 15:39:01.785  2901  2901 D libEGL  : loaded /vendor/lib64/egl/libGLESv2_adreno.so06-04 15:39:01.803  2901  2901 I Zygote  : preloading shared librarIEs...06-04 15:39:01.815  2901  2901 I Zygote  : Uninstalled ICU cache reference pinning...06-04 15:39:01.816  2901  2901 I Zygote  : Installed AndroIDKeyStoreProvIDer in 2ms.06-04 15:39:01.830  2901  2901 I Zygote  : Warmed up JCA provIDers in 13ms.06-04 15:39:01.830  2901  2901 D Zygote  : end preload06-04 15:39:01.831  2901  2901 I art     : Starting a blocking GC Explicit06-04 15:39:01.856  2901  2901 I art     : Explicit concurrent mark sweep GC freed 58740(6MB) AllocSpace objects, 134(2MB) LOS objects, 71% free, 3MB/11MB, paused 152us total 24.977ms06-04 15:39:01.858  2901  2901 I art     : Starting a blocking GC Explicit06-04 15:39:01.870  2901  2901 I art     : Explicit concurrent mark sweep GC freed 4484(191KB) AllocSpace objects, 1(24KB) LOS objects, 72% free, 2MB/10MB, paused 127us total 11.445ms06-04 15:39:01.873  2901  2908 I art     : Starting a blocking GC HeapTrim06-04 15:39:01.876  2901  2901 I art     : Starting a blocking GC Background
preloadResources()主要预加载的是com.androID.internal.R.array.preloaded_drawables和com.androID.internal.R.array.preloaded_color_state_Lists的,那这些资源的载体是什么,主要是加载framework-res.apk中的资源,各位也可以反编译看看。在应用程序中以com.androID.internal.R.xxx开头的资源,便是此时由Zygote加载到内存的。
private static Runnable forkSystemServer(String abiList, String socketname,            ZygoteServer zygoteServer) {        long capabilitIEs = posixCapabilitIEsAsBits(            OsConstants.CAP_IPC_LOCK,            OsConstants.CAP_KILL,            OsConstants.CAP_NET_admin,            OsConstants.CAP_NET_BIND_SERVICE,            OsConstants.CAP_NET_broADCAST,            OsConstants.CAP_NET_RAW,            OsConstants.CAP_SYS_MODulE,            OsConstants.CAP_SYS_NICE,            OsConstants.CAP_SYS_PTRACE,            OsConstants.CAP_SYS_TIME,            OsConstants.CAP_SYS_TTY_CONfig,            OsConstants.CAP_WAKE_ALARM,            OsConstants.CAP_BLOCK_SUSPEND        );        /* Containers run without some capabilitIEs, so drop any caps that are not available. */        StructCapUserheader header = new StructCapUserheader(                OsConstants._liNUX_CAPABIliTY_VERSION_3, 0);        StructCapUserData[] data;        try {            data = Os.capget(header);        } catch (ErrnoException ex) {            throw new RuntimeException("Failed to capget()", ex);        }        capabilitIEs &= ((long) data[0].effective) | (((long) data[1].effective) << 32);        /* Hardcoded command line to start the system server */        String args[] = {//参数准备            "--setuID=1000",            "--setgID=1000",            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",            "--capabilitIEs=" + capabilitIEs + "," + capabilitIEs,            "--nice-name=system_server",            "--runtime-args",            "--target-sdk-version=" + vmruntime.SDK_VERSION_CUR_DEVELOPMENT,            "com.androID.server.SystemServer",        };        ZygoteConnection.Arguments parsedArgs = null;        int pID;        try {            //用于解析参数,生成目标格式            parsedArgs = new ZygoteConnection.Arguments(args);            ZygoteConnection.applyDeBUGgerSystemProperty(parsedArgs);            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);            boolean profileSystemServer = SystemPropertIEs.getBoolean(                    "dalvik.vm.profilesystemserver", false);            if (profileSystemServer) {                parsedArgs.runtimeFlags |= Zygote.PROfile_SYstem_SERVER;            }            /* Request to fork the system server process */            //fork子进程,用于运行system_server            pID = Zygote.forkSystemServer(                    parsedArgs.uID, parsedArgs.gID,                    parsedArgs.gIDs,                    parsedArgs.runtimeFlags,                    null,                    parsedArgs.permittedCapabilitIEs,                    parsedArgs.effectiveCapabilitIEs);        } catch (IllegalArgumentException ex) {            throw new RuntimeException(ex);        }        /* For child process */        if (pID == 0) {//进入子进程system_server            if (hasSecondZygote(abiList)) {                waitForSecondaryZygote(socketname);            }            zygoteServer.closeServerSocket();            // 完成system_server进程剩余的工作            return handleSystemServerProcess(parsedArgs);        }        return null;    }

  当经过上述的步骤后,zygote进程内加载了preload()方法中的所有资源,当需要fork新进程时,采用COW(copy-on-write)技术,这里盗用已不在AndroID界的gityuan的一张图来说明:


  copy-on-write即写时拷贝技术,zygote在这里使用了copy-on-write技术可以提高应用运行速度,因为该种方式对运行在内存中的进程实现了最大程度的复用,并通过库共享有效降低了内存的使用量。也就是说当新的App通过fork()创建的的时候不进行内存的复制,这是因为复制内存的开销是很大的,此时子进程只需要共享父进程的内存空间即可,因为这个时候他们没有差异。而当子进程需要需要修改共享内存信息时,此时才开始将内存信息复制到自己的内存空间中,并进行修改。感觉很高大上啊,这也就是为啥我们的App里面也能使用预加载的资源,so库等。这下大伙明白为啥我们能import com.androID.internal.R.xxx的资源了吗。


2.4 forkSystemServer

  该代码主要就是准备参数,并启动 system_server 进程,后续启动的AndroID Java 系统服务都将驻留在该进程中,它是是AndroID framework核心。这里主要设置了system_server 进程uID和gID,process name,class name。并且从zygote进程fork新进程后,需要关闭zygote原有的socket,另外,对于有两个zygote进程情况,需等待第2个zygote创建完成。

06-04 15:39:01.920  2901  2901 I Zygote  : System server process 2910 has been created06-04 15:39:01.923  2901  2901 I Zygote  : Accepting command socket connections06-04 15:39:01.940  2910  2910 I Zygote  : Process: zygote socket opened, supported ABIS: armeabi-v7a,armeabi06-04 15:39:01.941  2910  2910 I InstallerConnection: connecting...06-04 15:39:01.942   771   771 I         : new connection06-04 15:39:01.945  2910  2910 I InstallerConnection: disconnecting...06-04 15:39:01.945   771   771 E         : eof06-04 15:39:01.945   771   771 E         : Failed to read size06-04 15:39:01.945   771   771 I         : closing connection06-04 15:39:01.955  2910  2910 V appproc : App process: starting thread pool.06-04 15:39:01.959  2910  2910 I SystemServer: Entered the AndroID system server!
127|msm8953_64:/ # ps  1515USER      PID   PPID  VSIZE  RSS   WCHAN              PC  namesystem    1515  762   2384060 133700 SyS_epoll_ 0000000000 S system_server

  这里我有一个疑问就是设置的system_server进程的uID=1000,gID=1000可是实际ps查看的并不是,这个希望大伙一起探讨。

    Runnable runSelectLoop(String abiList) {        ArrayList<fileDescriptor> fds = new ArrayList<fileDescriptor>();        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();        		//sServerSocket是socket通信中的服务端,即zygote进程。保存到fds[0]        fds.add(mServerSocket.getfileDescriptor());        peers.add(null);        while (true) {            StructPollfd[] pollFds = new StructPollfd[fds.size()];            for (int i = 0; i < pollFds.length; ++i) {                pollFds[i] = new StructPollfd();                pollFds[i].fd = fds.get(i);                pollFds[i].events = (short) PolliN;            }            try {            	//处理轮询状态,当pollFds有事件到来则往下执行,否则阻塞在这里                Os.poll(pollFds, -1);            } catch (ErrnoException ex) {                throw new RuntimeException("poll Failed", ex);            }            for (int i = pollFds.length - 1; i >= 0; --i) {                //采用I/O多路复用机制,当接收到客户端发出连接请求 或者数据处理请求到来,则往下执行;                // 否则进入continue,跳出本次循环。                if ((pollFds[i].revents & PolliN) == 0) {                    continue;                }                if (i == 0) {                   //即fds[0],代表的是sServerSocket,则意味着有客户端连接请求;                   // 则创建ZygoteConnection对象,并添加到fds。                    ZygoteConnection newPeer = acceptCommandPeer(abiList);                    peers.add(newPeer);                    fds.add(newPeer.getfileDesciptor());                } else {                    try {                    	//i>0,则代表通过socket接收来自对端的数据,并执行相应 *** 作                        ZygoteConnection connection = peers.get(i);                        final Runnable command = connection.processOneCommand(this);                        if (mIsForkChild) {                            // We're in the child. We should always have a command to run at this                            // stage if processOneCommand hasn't called "exec".                            if (command == null) {                                throw new IllegalStateException("command == null");                            }                            return command;                        } else {                            // We're in the server - we should never have any commands to run.                            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(i);                                fds.remove(i);//处理完则从fds中移除该文件描述符                            }                        }                     } catch (Exception e) {                        if (!mIsForkChild) {                            // 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);                            // Make sure the socket is closed so that the other end kNows immediately                            // that something has gone wrong and doesn't time out waiting for a                            // response.                            ZygoteConnection conn = peers.remove(i);                            conn.closeSocket();                            fds.remove(i);                        } else {                            // We're in the child so any exception caught here has happened post                            // fork and before we execute ActivityThread.main (or any other main()                            // method). Log the details of the exception and bring down the process.                            Log.e(TAG, "Caught post-fork exception in child process.", e);                            throw e;                        }                    } finally {                        // reset the child flag, in the event that the child process is a child-                        // zygote. The flag will not be consulted this loop pass after the Runnable                        // is returned.                        mIsForkChild = false;                    }                }            }        }    }                 

2.5 ZygoteServer.runSelectLoop

  该代码定义在frameworks/base/core/java/com/androID/internal/os/ZygoteServer.java,在这个阶段zygote将进入循环状态等待AMS来和zygote进行通信,从而孵化新的App。

Runnable processOneCommand(ZygoteServer zygoteServer) {    String args[];    Arguments parsedArgs = null;    fileDescriptor[] descriptors;    try {        //读取socket客户端发送过来的参数列表        args = readArgumentList();        descriptors = mSocket.getAncillaryfileDescriptors();    } catch (IOException ex) {        ...        throw new IllegalStateException("IOException on command socket", ex);    }    ...        //将socket客户端传递过来的参数,解析成Arguments对象格式        parsedArgs = new Arguments(args);        ...        pID = Zygote.forkAndSpecialize(parsedArgs.uID, parsedArgs.gID, parsedArgs.gIDs,                parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,                parsedArgs.nicename, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,                parsedArgs.instructionSet, parsedArgs.appDataDir);        try {            if (pID == 0) {                // in child                //子进程执行                zygoteServer.setForkChild();                zygoteServer.closeServerSocket();                IoUtils.closeQuIEtly(serverPipeFd);                serverPipeFd = null;                return handleChildProc(parsedArgs, descriptors, childPipeFd,                        parsedArgs.startChildZygote);            } else {                // In the parent. A pID < 0 indicates a failure and will be handled in                // handleParentProc.                //父进程执行                IoUtils.closeQuIEtly(childPipeFd);                childPipeFd = null;                handleParentProc(pID, descriptors, serverPipeFd);                return null;            }        } finally {            IoUtils.closeQuIEtly(childPipeFd);            IoUtils.closeQuIEtly(serverPipeFd);        }}

  从上面的代码可以看出,Zygote采用高效的I/O多路复用机制,保证在没有客户端连接请求或数据处理时休眠,否则响应客户端的请求。


2.6 ZygoteConnection.processOneCommand

  该代码定义在frameworks/base/core/java/com/androID/internal/os/ZygoteConnection.java中,解析socket客户端即AMS传递过来的参数,然后调用forkAndSpecialize创建App进程。

[+++]


总结

  到这里zygote启动就基本告一段落了,zygote启动的调用流程图如下所示:


细数下来,zygote进程启动主要干了如下的相关大事:

解析init.zygotexxx.rc传递过来的参数,创建AppRuntime并调用AppRuntime.start()方法;调用AndroIDRuntime的startVM()方法创建虚拟机,再调用startReg()注册JNI函数虚拟机和JNI环境构建后以后,通过JNI方式调用ZygoteInit.main(),正式进入的Java世界调用registerServerSocketFromEnv()建立socket通道,zygote作为通信的服务端,用于响应客户端请求,这里大伙可以思考一个问题就是为啥用的是zygote通信而不是binder preload()预加载通用类、drawable和color资源、openGL以及共享库以及WebVIEw,用于提高app启动效率zygote完毕大部分工作,接下来再通过forkSystemServer(),fork得力帮手system_server进程,也是上层framework的运行载体zygote功成身退,调用runSelectLoop(),随时待命,当接收到请求创建新进程请求时立即唤醒并执行相应工作。

写在最后

  AndroID 9 zygote启动就告一段落了,但是这个只是一个开端,因为其中的forkSystemServer启动system_server和forkAndSpecialize启动App并没有讲解,这个牵涉的东西太多了,现在功力不足以将其写清楚,所以接下来会修炼内功,在接下来的篇章中力求讲上述两个遗留的问题说透彻。如果对给位有帮助欢迎点赞一个,如果写得有问题也欢迎多多指正。未完待续,下个篇章再见。

总结

以上是内存溢出为你收集整理的Android 9 (P) Zygote进程启动源码分析指南二全部内容,希望文章能够帮你解决Android 9 (P) Zygote进程启动源码分析指南二所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Android 9 (P) Zygote进程启动源码分析指南二_app_内存溢出

Android 9 (P) Zygote进程启动源码分析指南二

Android 9 (P) Zygote进程启动源码分析指南二,第1张

概述     Android9Zygote进程启动源码分析指南二前言  各位老司机们,现在闲下来终于有时间接着续写Android9Zygote进程启动源码分析指南二了,在前面的篇章AndroidPZygote进程启动源码分析指南一中,我们已经讲解了zygote启动的前面阶段主要是为了孵化Android世界做      AndroID 9 Zygote进程启动源码分析指南二

前言

  各位老司机们,现在闲下来终于有时间接着续写AndroID 9 Zygote进程启动源码分析指南二了,在前面的篇章Android P Zygote进程启动源码分析指南一中,我们已经讲解了zygote启动的前面阶段主要是为了孵化AndroID世界做的前期准备工作,大概的内容如下所示:

Zygote进程启动流程整体概括Zygote 进程从何而来zygote创建参数解析创建虚拟机注册JNI函数

都说zygote开创了AndroID的世界,创造了五彩缤纷的AndroID生态,孵化了各式的上层App。那么本篇将会带领大伙看看,我们的zygote是怎么做到的!要的罗,让我们开干。

注意:本文演示的代码是AndroID P高通msm8953平台源码。其中涉及的源码路径如下:

frameworks/base/include/androID_runtime/AndroIDRuntime.hframeworks/base/core/jni/AndroIDRuntime.cppframeworks/base//core/java/com/androID/internal/os/ZygoteInit.java


一. 通过JNI反射启动ZygoteInit的main方法

  在前面zygote启动阶段已经创建好了虚拟机,注册好了系统JNI函数,一切就绪只欠东风了!在接下来通过JNI的反射调用Java的类了。关于JNI的语法如果大伙有不熟悉的可以参见系列篇章JNI/NDK入门一锅端。


1.1 JNI反射启动ZygoteInit的main

  还是熟悉的味道,还是原来的配方,经过一顿猛如虎的 *** 作最终调用CallStaticVoIDMethod反射从而调用ZygoteInit的main函数,

char* AndroIDRuntime::toSlashClassname(const char* classname){    char* result = strdup(classname);    for (char* cp = result; *cp != '	private static final String SOCKET_name_ARG = "--socket-name=";	public static voID main(String argv[]) {			....            String socketname = "zygote";            String abiList = null;            boolean enableLazyPreload = false;            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.");            }            zygoteServer.registerServerSocketFromEnv(socketname);//注册zygote进程和AMS交互的通道            ......            preload(boottimingsTraceLog);//预加载类和资源			gcAndFinalize();//GC *** 作            if (startSystemServer) {                Runnable r = forkSystemServer(abiList, socketname, zygoteServer);//启动system_server                // {@code r == null} in the parent (zygote) process, and {@code r != null} in the                // child (system_server) process.                if (r != null) {                    r.run();                    return;                }            }            Log.i(TAG, "Accepting command socket connections");            // The select loop returns early in the child process after a fork and            // loops forever in the zygote.            caller = zygoteServer.runSelectLoop(abiList);//进入循环模式	......}'; cp++) {        if (*cp == '.') {            *cp = '/';        }    }    return result;}voID AndroIDRuntime::start(const char* classname, const Vector<String8>& options, bool zygote){	......    /*     * We want to call main() with a String array with arguments in it.     * At present we have two arguments, the class name and an option string.     * Create an array to hold them.     */    jclass stringClass;//申明一些jni变量,没有什么好说的    jobjectArray strArray;    Jstring classnameStr;    stringClass = env->FindClass("java/lang/String");    assert(stringClass != NulL);    strArray = env->NewObjectArray(options.size() + 1, stringClass, NulL);    assert(strArray != NulL);    classnameStr = env->NewStringUTF(classname);    assert(classnameStr != NulL);    env->SetobjectArrayElement(strArray, 0, classnameStr);    for (size_t i = 0; i < options.size(); ++i) {        Jstring optionsstr = env->NewStringUTF(options.itemAt(i).string());        assert(optionsstr != NulL);        env->SetobjectArrayElement(strArray, i + 1, optionsstr);    }    /*     * Start VM.  This thread becomes the main thread of the VM, and will     * not return until the VM exits.     */     /*     *将字符中的.转换为/,我们前面传递过来的字符串是com.androID.internal.os.ZygoteInit,     *通过这里转换为com/androID/internal/os/ZygoteInit,不要问我为啥要这样这个是JNI的规范     */    char* slashClassname = toSlashClassname(classname != NulL ? classname : "");    jclass startClass = env->FindClass(slashClassname);    if (startClass == NulL) {        ALOGE("JavaVM unable to locate class '%s'\n", slashClassname);        /* keep going */    } else {        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 {            env->CallStaticVoIDMethod(startClass, startMeth, strArray);//调用main方法#if 0            if (env->ExceptionCheck())                threadExitUncaughtException(env);#endif        }    }    free(slashClassname);    ALOGD("Shutting down VM\n");    if (mJavaVM->DetachCurrentThread() != JNI_OK)//退出当前线程        ALOGW("Warning: unable to detach main thread\n");    if (mJavaVM->DestroyJavaVM() != 0)//等待所有线程结束关闭虚拟机        ALOGW("Warning: VM dID not shut down cleanly\n");	......}


二. 登陆Java世界

  兜兜转转,调用CallStaticVoIDMethod终于登陆Java的世界了,Java世界真香,让我们畅游一番。


2.1 ZygoteInit.main

  这里不过多详细解释,在main方法中主要做了如下几件事情:

解析参数调用zygoteServer.registerServerSocketFromEnv创建zygote通信的服务端调用forkSystemServer启动system_serverzygoteServer.runSelectLoop进入循环模式
    /**     * Registers a server socket for zygote command connections. This locates the server socket     * file descriptor through an ANDROID_SOCKET_ environment variable.     *     * @throws RuntimeException when open fails     */    voID registerServerSocketFromEnv(String socketname) {        if (mServerSocket == null) {            int fileDesc;            final String fullSocketname = ANDROID_SOCKET_PREFIX + socketname;            try {                String env = System.getenv(fullSocketname);                fileDesc = Integer.parseInt(env);            } catch (RuntimeException ex) {                throw new RuntimeException(fullSocketname + " unset or invalID", ex);            }            try {                fileDescriptor fd = new fileDescriptor();                fd.setInt$(fileDesc);//设置文件描述符                mServerSocket = new LocalServerSocket(fd);//创建socket本地服务端                mCloseSocketFd = true;            } catch (IOException ex) {                throw new RuntimeException(                        "Error binding to local socket '" + fileDesc + "'", ex);            }        }    }

2.2 zygoteServer.registerServerSocketFromEnv

  该代码的路径是frameworks/base/core/java/com/androID/internal/os/ZygoteServer.java,这里主要是通过解析zygote启动传入的变量值得到 zygote 文件描述符,根据该文件描述符创建 socket,用来和 ActivityManagerService 通信。AMS 通过 Process.start 来创建新的进程,而Process.start 会先通过 socket 连接到 zygote 进程,并最终由 zygote 完成进程创建。这里传入的-为-socket-name=zygote.。

//定义在frameworks/base/core/java/androID/os/Process.java    public static final ProcessstartResult start(final String processClass,                                  final String nicename,                                  int uID, int gID, int[] gIDs,                                  int runtimeFlags, int mountExternal,                                  int targetSdkVersion,                                  String seInfo,                                  String abi,                                  String instructionSet,                                  String appDataDir,                                  String invokeWith,                                  String[] zygoteArgs) {        return zygoteProcess.start(processClass, nicename, uID, gID, gIDs,                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,                    abi, instructionSet, appDataDir, invokeWith, zygoteArgs);    }	//定义在frameworks/base/core/java/androID/os/ZygoteProcess.java    public final Process.ProcessstartResult start(final String processClass,                                                  final String nicename,                                                  int uID, int gID, int[] gIDs,                                                  int runtimeFlags, int mountExternal,                                                  int targetSdkVersion,                                                  String seInfo,                                                  String abi,                                                  String instructionSet,                                                  String appDataDir,                                                  String invokeWith,                                                  String[] zygoteArgs) {        try {            return startViaZygote(processClass, nicename, uID, gID, gIDs,                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,                    abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,                    zygoteArgs);        } catch (ZygoteStartFailedEx ex) {            Log.e(LOG_TAG,                    "Starting VM process through Zygote Failed");            throw new RuntimeException(                    "Starting VM process through Zygote Failed", ex);        }    }    private Process.ProcessstartResult startViaZygote(final String processClass,                                                      final String nicename,                                                      final int uID, final int gID,                                                      final int[] gIDs,                                                      int runtimeFlags, int mountExternal,                                                      int targetSdkVersion,                                                      String seInfo,                                                      String abi,                                                      String instructionSet,                                                      String appDataDir,                                                      String invokeWith,                                                      boolean startChildZygote,                                                      String[] extraArgs)                                                      throws ZygoteStartFailedEx {	......	        synchronized(mlock) {            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);        }	......}           private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {        Preconditions.checkState(Thread.holdsLock(mlock), "ZygoteProcess lock not held");        if (primaryZygoteState == null || primaryZygoteState.isClosed()) {            try {                primaryZygoteState = ZygoteState.connect(mSocket);            } catch (IOException ioe) {                throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);            }            maybeSetAPIBlackListExemptions(primaryZygoteState, false);            maybeSetHIDdenAPIAccessLogSampleRate(primaryZygoteState);        }        if (primaryZygoteState.matches(abi)) {            return primaryZygoteState;        }        // The primary zygote dIDn't match. Try the secondary.        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {            try {                secondaryZygoteState = ZygoteState.connect(mSecondarySocket);            } catch (IOException ioe) {                throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);            }            maybeSetAPIBlackListExemptions(secondaryZygoteState, false);            maybeSetHIDdenAPIAccessLogSampleRate(secondaryZygoteState);        }        if (secondaryZygoteState.matches(abi)) {            return secondaryZygoteState;        }        throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);    }                                               

  LocalSocket是AndroID的妈咪谷歌为我们带来了,比Java本身的socket效率要高,没有经过协议栈,是AndroID自己实现的类似共享内存一样的东东,在传输大量数据的时候就需要用到,关于LocalSocket的介绍和使用大伙可以参考篇章Android Framework层和Native层通过LocalSocket实现通信。

  我们知道ActivityManagerService 通过 Process.start 来创建新的进程,而 Process.start 会先通过socket 连接到 zygote 进程,并最终由 zygote 完成进程创建。如下是App进程创建请求Zygote创建新的进程。

//预加载位于/system/etc/preloaded-classes文件中的类    preloadClasses();    //预加载资源,包含drawable和color资源    preloadResources();    //预加载OpenGL    preloadOpenGL();    //通过System.loadlibrary()方法,    //预加载"androID","compiler_rt","jnigraphics"这3个共享库    preloadSharedlibrarIEs();    //预加载 文本连接符资源    preloadTextResources();    //仅用于zygote进程,用于内存共享的进程    WebVIEwFactory.prepareWebVIEwInZygote();

  我不知道大伙读到这里是否有一个疑问,为啥AMS和Zygote通信使用的是socket而不是AndroID的杜门绝技Binder,这个当初学习源码的时候也是我思考的,来大伙思考思考为啥AndroID的设计者是这么设计的呢,我认为主要有如下几个方面的考虑:

zygote比service manager先启动,从这个意义触发,zygote没有service manager可以注册binder,所以没有办法binderzygote进程和service manager进程都是由init进程启动的,那怕先启动service manager,也不能保证zygote起来的时候service manager启动好了,这样就需要额外的同步同时这个socket的所有者是root,用户组是system,只有系统权限的用户才能读写,这又多了一个安全保障(这里的socket是unix域的socket,而不是internet域的socket)最最主要的是zygote是通过fork生成进程的,而多线程是不允许fork的,可能造成死锁,同时Binder又是多线程,为了避免这些麻烦所以干脆使用socket
2.3 preload预加载

  预加载类和资源,androID Java 进程都是由 zygote 进程 fork,zygote 通过预加载类和资源可以加快子进程的执行速度和优化内存。因为预加载的类和资源较多,在开机优化过程中也需要重点关注 preload 的耗时。

[LandroID.accounts.Account;[LandroID.animation.Animator;[LandroID.animation.Keyframe$floatKeyframe;[LandroID.animation.Keyframe$IntKeyframe;[LandroID.animation.Keyframe$ObjectKeyframe;[LandroID.animation.Keyframe;[LandroID.animation.PropertyValuesHolder;[LandroID.app.LoaderManagerImpl;[LandroID.content.ContentProvIDerResult;[LandroID.content.ContentValues;[LandroID.content.Intent;[LandroID.content.UndoOwner;[LandroID.content.pm.ActivityInfo;[LandroID.content.pm.ConfigurationInfo;[LandroID.content.pm.FeatureGroupInfo;[LandroID.content.pm.FeatureInfo;[LandroID.content.pm.InstrumentationInfo;[LandroID.content.pm.PathPermission;
preloadClasses()预加载的文件路径为/system/etc/preloaded-classes,预加载比较多有大概4000多个类,这里对于类的加载主要通过Class.forname()方法来进行的。
06-04 15:39:01.041  2901  2901 D Zygote  : begin preload06-04 15:39:01.041  2901  2901 I Zygote  : Installing ICU cache reference pinning...06-04 15:39:01.041  2901  2901 I Zygote  : preloading ICU data...06-04 15:39:01.061  2901  2901 I Zygote  : preloading classes...06-04 15:39:01.065  2901  2901 W Zygote  : Class not found for preloading: [LandroID.vIEw.display$colortransform;06-04 15:39:01.150  2901  2901 E Typeface: Error mapPing Font file /system/Fonts/DroIDSansFallback.ttf06-04 15:39:01.291  2901  2901 I art     : Thread[1,tID=2901,Native,Thread*=0x7f77696a00,peer=0x12c060d0,"main"] recursive attempt to load library "/system/lib64/libmedia_jni.so"06-04 15:39:01.291  2901  2901 D MtpDeviceJNI: register_androID_mtp_MtpDevice06-04 15:39:01.292  2901  2901 I art     : Thread[1,tID=2901,Native,Thread*=0x7f77696a00,peer=0x12c060d0,"main"] recursive attempt to load library "/system/lib64/libmedia_jni.so"06-04 15:39:01.292  2901  2901 I art     : Thread[1,tID=2901,Native,Thread*=0x7f77696a00,peer=0x12c060d0,"main"] recursive attempt to load library "/system/lib64/libmedia_jni.so"06-04 15:39:01.316  2901  2901 D         : Environment::getoverlayDir() = /system/vendor/Default/system/vendor/overlay06-04 15:39:01.352  2901  2901 W Zygote  : Class not found for preloading: androID.vIEw.display$colortransform06-04 15:39:01.352  2901  2901 W Zygote  : Class not found for preloading: androID.vIEw.display$colortransform6-04 15:39:01.449  2901  2901 I System  : Loaded time zone names for "" in 32ms (30ms in ICU)06-04 15:39:01.469  2901  2901 I System  : Loaded time zone names for "en_US" in 19ms (16ms in ICU)06-04 15:39:01.491  2901  2901 I System  : Loaded time zone names for "zh_CN" in 22ms (20ms in ICU)06-04 15:39:01.518  2901  2901 I Zygote  : ...preloaded 4158 classes in 457ms.06-04 15:39:01.518  2901  2901 I art     : vmruntime.preloadDexCaches starting06-04 15:39:01.572  1374  1419 E slim_daemon: [NDK] bindNDKSensors: Sensor server is unavailable.06-04 15:39:01.664  2901  2901 I art     : vmruntime.preloadDexCaches strings total=292199 before=40345 after=4034506-04 15:39:01.665  2901  2901 I art     : vmruntime.preloadDexCaches types total=24111 before=8040 after=807506-04 15:39:01.665  2901  2901 I art     : vmruntime.preloadDexCaches fIElds total=115222 before=41429 after=4166006-04 15:39:01.665  2901  2901 I art     : vmruntime.preloadDexCaches methods total=201879 before=83038 after=8378206-04 15:39:01.665  2901  2901 I art     : vmruntime.preloadDexCaches finished06-04 15:39:01.666  2901  2901 I Zygote  : preloading resources...06-04 15:39:01.682  2901  2901 W Resources: Preloaded drawable resource #0x108025d (androID:drawable/dialog_background_material) that varIEs with configuration!!06-04 15:39:01.683  2901  2901 W Resources: Preloaded drawable resource #0x1080299 (androID:drawable/editBox_dropdown_background_dark) that varIEs with configuration!!06-04 15:39:01.686  2901  2901 W Resources: Preloaded color resource #0x10600e2 (androID:color/material_grey_800) that varIEs with configuration!!06-04 15:39:01.686  2901  2901 W Resources: Preloaded drawable resource #0x10802ca (androID:drawable/floating_popup_background_dark) that varIEs with configuration!!06-04 15:39:01.694  2901  2901 W Resources: Preloaded drawable resource #0x108030c (androID:drawable/ic_clear_Disabled) that varIEs with configuration!!06-04 15:39:01.694  2901  2901 W Resources: Preloaded drawable resource #0x1080311 (androID:drawable/ic_clear_normal) that varIEs with configuration!!06-04 15:39:01.698  2901  2901 W Resources: Preloaded drawable resource #0x108035a (androID:drawable/ic_go) that varIEs with configuration!!06-04 15:39:01.704  2901  2901 W Resources: Preloaded drawable resource #0x1080038 (androID:drawable/ic_menu_close_clear_cancel) that varIEs with configuration!!06-04 15:39:01.707  2901  2901 W Resources: Preloaded drawable resource #0x1080045 (androID:drawable/ic_menu_more) that varIEs with configuration!!06-04 15:39:01.714  2901  2901 W Resources: Preloaded drawable resource #0x10804f1 (androID:drawable/menu_background_fill_parent_wIDth) that varIEs with configuration!!06-04 15:39:01.744  2901  2901 W Resources: Preloaded drawable resource #0x1080787 (androID:drawable/text_edit_paste_window) that varIEs with configuration!!06-04 15:39:01.748  2901  2901 W Resources: Preloaded drawable resource #0x1080096 (androID:drawable/toast_frame) that varIEs with configuration!!06-04 15:39:01.748  2901  2901 I Zygote  : ...preloaded 114 resources in 82ms.06-04 15:39:01.752  2901  2901 W Resources: Preloaded color resource #0x106010b (androID:color/background_cache_hint_selector_material_dark) that varIEs with configuration!!06-04 15:39:01.753  2901  2901 W Resources: Preloaded color resource #0x1060110 (androID:color/btn_default_material_dark) that varIEs with configuration!!06-04 15:39:01.755  2901  2901 I Zygote  : ...preloaded 41 resources in 7ms.06-04 15:39:01.757  2901  2901 D libEGL  : loaded /vendor/lib64/egl/libEGL_adreno.so06-04 15:39:01.773  2901  2901 D libEGL  : loaded /vendor/lib64/egl/libGLESv1_CM_adreno.so06-04 15:39:01.785  2901  2901 D libEGL  : loaded /vendor/lib64/egl/libGLESv2_adreno.so06-04 15:39:01.803  2901  2901 I Zygote  : preloading shared librarIEs...06-04 15:39:01.815  2901  2901 I Zygote  : Uninstalled ICU cache reference pinning...06-04 15:39:01.816  2901  2901 I Zygote  : Installed AndroIDKeyStoreProvIDer in 2ms.06-04 15:39:01.830  2901  2901 I Zygote  : Warmed up JCA provIDers in 13ms.06-04 15:39:01.830  2901  2901 D Zygote  : end preload06-04 15:39:01.831  2901  2901 I art     : Starting a blocking GC Explicit06-04 15:39:01.856  2901  2901 I art     : Explicit concurrent mark sweep GC freed 58740(6MB) AllocSpace objects, 134(2MB) LOS objects, 71% free, 3MB/11MB, paused 152us total 24.977ms06-04 15:39:01.858  2901  2901 I art     : Starting a blocking GC Explicit06-04 15:39:01.870  2901  2901 I art     : Explicit concurrent mark sweep GC freed 4484(191KB) AllocSpace objects, 1(24KB) LOS objects, 72% free, 2MB/10MB, paused 127us total 11.445ms06-04 15:39:01.873  2901  2908 I art     : Starting a blocking GC HeapTrim06-04 15:39:01.876  2901  2901 I art     : Starting a blocking GC Background
preloadResources()主要预加载的是com.androID.internal.R.array.preloaded_drawables和com.androID.internal.R.array.preloaded_color_state_Lists的,那这些资源的载体是什么,主要是加载framework-res.apk中的资源,各位也可以反编译看看。在应用程序中以com.androID.internal.R.xxx开头的资源,便是此时由Zygote加载到内存的。
private static Runnable forkSystemServer(String abiList, String socketname,            ZygoteServer zygoteServer) {        long capabilitIEs = posixCapabilitIEsAsBits(            OsConstants.CAP_IPC_LOCK,            OsConstants.CAP_KILL,            OsConstants.CAP_NET_admin,            OsConstants.CAP_NET_BIND_SERVICE,            OsConstants.CAP_NET_broADCAST,            OsConstants.CAP_NET_RAW,            OsConstants.CAP_SYS_MODulE,            OsConstants.CAP_SYS_NICE,            OsConstants.CAP_SYS_PTRACE,            OsConstants.CAP_SYS_TIME,            OsConstants.CAP_SYS_TTY_CONfig,            OsConstants.CAP_WAKE_ALARM,            OsConstants.CAP_BLOCK_SUSPEND        );        /* Containers run without some capabilitIEs, so drop any caps that are not available. */        StructCapUserheader header = new StructCapUserheader(                OsConstants._liNUX_CAPABIliTY_VERSION_3, 0);        StructCapUserData[] data;        try {            data = Os.capget(header);        } catch (ErrnoException ex) {            throw new RuntimeException("Failed to capget()", ex);        }        capabilitIEs &= ((long) data[0].effective) | (((long) data[1].effective) << 32);        /* Hardcoded command line to start the system server */        String args[] = {//参数准备            "--setuID=1000",            "--setgID=1000",            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",            "--capabilitIEs=" + capabilitIEs + "," + capabilitIEs,            "--nice-name=system_server",            "--runtime-args",            "--target-sdk-version=" + vmruntime.SDK_VERSION_CUR_DEVELOPMENT,            "com.androID.server.SystemServer",        };        ZygoteConnection.Arguments parsedArgs = null;        int pID;        try {            //用于解析参数,生成目标格式            parsedArgs = new ZygoteConnection.Arguments(args);            ZygoteConnection.applyDeBUGgerSystemProperty(parsedArgs);            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);            boolean profileSystemServer = SystemPropertIEs.getBoolean(                    "dalvik.vm.profilesystemserver", false);            if (profileSystemServer) {                parsedArgs.runtimeFlags |= Zygote.PROfile_SYstem_SERVER;            }            /* Request to fork the system server process */            //fork子进程,用于运行system_server            pID = Zygote.forkSystemServer(                    parsedArgs.uID, parsedArgs.gID,                    parsedArgs.gIDs,                    parsedArgs.runtimeFlags,                    null,                    parsedArgs.permittedCapabilitIEs,                    parsedArgs.effectiveCapabilitIEs);        } catch (IllegalArgumentException ex) {            throw new RuntimeException(ex);        }        /* For child process */        if (pID == 0) {//进入子进程system_server            if (hasSecondZygote(abiList)) {                waitForSecondaryZygote(socketname);            }            zygoteServer.closeServerSocket();            // 完成system_server进程剩余的工作            return handleSystemServerProcess(parsedArgs);        }        return null;    }

  当经过上述的步骤后,zygote进程内加载了preload()方法中的所有资源,当需要fork新进程时,采用COW(copy-on-write)技术,这里盗用已不在AndroID界的gityuan的一张图来说明:


  copy-on-write即写时拷贝技术,zygote在这里使用了copy-on-write技术可以提高应用运行速度,因为该种方式对运行在内存中的进程实现了最大程度的复用,并通过库共享有效降低了内存的使用量。也就是说当新的App通过fork()创建的的时候不进行内存的复制,这是因为复制内存的开销是很大的,此时子进程只需要共享父进程的内存空间即可,因为这个时候他们没有差异。而当子进程需要需要修改共享内存信息时,此时才开始将内存信息复制到自己的内存空间中,并进行修改。感觉很高大上啊,这也就是为啥我们的App里面也能使用预加载的资源,so库等。这下大伙明白为啥我们能import com.androID.internal.R.xxx的资源了吗。


2.4 forkSystemServer

  该代码主要就是准备参数,并启动 system_server 进程,后续启动的AndroID Java 系统服务都将驻留在该进程中,它是是AndroID framework核心。这里主要设置了system_server 进程uID和gID,process name,class name。并且从zygote进程fork新进程后,需要关闭zygote原有的socket,另外,对于有两个zygote进程情况,需等待第2个zygote创建完成。

06-04 15:39:01.920  2901  2901 I Zygote  : System server process 2910 has been created06-04 15:39:01.923  2901  2901 I Zygote  : Accepting command socket connections06-04 15:39:01.940  2910  2910 I Zygote  : Process: zygote socket opened, supported ABIS: armeabi-v7a,armeabi06-04 15:39:01.941  2910  2910 I InstallerConnection: connecting...06-04 15:39:01.942   771   771 I         : new connection06-04 15:39:01.945  2910  2910 I InstallerConnection: disconnecting...06-04 15:39:01.945   771   771 E         : eof06-04 15:39:01.945   771   771 E         : Failed to read size06-04 15:39:01.945   771   771 I         : closing connection06-04 15:39:01.955  2910  2910 V appproc : App process: starting thread pool.06-04 15:39:01.959  2910  2910 I SystemServer: Entered the AndroID system server!
127|msm8953_64:/ # ps  1515USER      PID   PPID  VSIZE  RSS   WCHAN              PC  namesystem    1515  762   2384060 133700 SyS_epoll_ 0000000000 S system_server

  这里我有一个疑问就是设置的system_server进程的uID=1000,gID=1000可是实际ps查看的并不是,这个希望大伙一起探讨。

    Runnable runSelectLoop(String abiList) {        ArrayList<fileDescriptor> fds = new ArrayList<fileDescriptor>();        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();        		//sServerSocket是socket通信中的服务端,即zygote进程。保存到fds[0]        fds.add(mServerSocket.getfileDescriptor());        peers.add(null);        while (true) {            StructPollfd[] pollFds = new StructPollfd[fds.size()];            for (int i = 0; i < pollFds.length; ++i) {                pollFds[i] = new StructPollfd();                pollFds[i].fd = fds.get(i);                pollFds[i].events = (short) PolliN;            }            try {            	//处理轮询状态,当pollFds有事件到来则往下执行,否则阻塞在这里                Os.poll(pollFds, -1);            } catch (ErrnoException ex) {                throw new RuntimeException("poll Failed", ex);            }            for (int i = pollFds.length - 1; i >= 0; --i) {                //采用I/O多路复用机制,当接收到客户端发出连接请求 或者数据处理请求到来,则往下执行;                // 否则进入continue,跳出本次循环。                if ((pollFds[i].revents & PolliN) == 0) {                    continue;                }                if (i == 0) {                   //即fds[0],代表的是sServerSocket,则意味着有客户端连接请求;                   // 则创建ZygoteConnection对象,并添加到fds。                    ZygoteConnection newPeer = acceptCommandPeer(abiList);                    peers.add(newPeer);                    fds.add(newPeer.getfileDesciptor());                } else {                    try {                    	//i>0,则代表通过socket接收来自对端的数据,并执行相应 *** 作                        ZygoteConnection connection = peers.get(i);                        final Runnable command = connection.processOneCommand(this);                        if (mIsForkChild) {                            // We're in the child. We should always have a command to run at this                            // stage if processOneCommand hasn't called "exec".                            if (command == null) {                                throw new IllegalStateException("command == null");                            }                            return command;                        } else {                            // We're in the server - we should never have any commands to run.                            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(i);                                fds.remove(i);//处理完则从fds中移除该文件描述符                            }                        }                     } catch (Exception e) {                        if (!mIsForkChild) {                            // 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);                            // Make sure the socket is closed so that the other end kNows immediately                            // that something has gone wrong and doesn't time out waiting for a                            // response.                            ZygoteConnection conn = peers.remove(i);                            conn.closeSocket();                            fds.remove(i);                        } else {                            // We're in the child so any exception caught here has happened post                            // fork and before we execute ActivityThread.main (or any other main()                            // method). Log the details of the exception and bring down the process.                            Log.e(TAG, "Caught post-fork exception in child process.", e);                            throw e;                        }                    } finally {                        // reset the child flag, in the event that the child process is a child-                        // zygote. The flag will not be consulted this loop pass after the Runnable                        // is returned.                        mIsForkChild = false;                    }                }            }        }    }                 

2.5 ZygoteServer.runSelectLoop

  该代码定义在frameworks/base/core/java/com/androID/internal/os/ZygoteServer.java,在这个阶段zygote将进入循环状态等待AMS来和zygote进行通信,从而孵化新的App。

Runnable processOneCommand(ZygoteServer zygoteServer) {    String args[];    Arguments parsedArgs = null;    fileDescriptor[] descriptors;    try {        //读取socket客户端发送过来的参数列表        args = readArgumentList();        descriptors = mSocket.getAncillaryfileDescriptors();    } catch (IOException ex) {        ...        throw new IllegalStateException("IOException on command socket", ex);    }    ...        //将socket客户端传递过来的参数,解析成Arguments对象格式        parsedArgs = new Arguments(args);        ...        pID = Zygote.forkAndSpecialize(parsedArgs.uID, parsedArgs.gID, parsedArgs.gIDs,                parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,                parsedArgs.nicename, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,                parsedArgs.instructionSet, parsedArgs.appDataDir);        try {            if (pID == 0) {                // in child                //子进程执行                zygoteServer.setForkChild();                zygoteServer.closeServerSocket();                IoUtils.closeQuIEtly(serverPipeFd);                serverPipeFd = null;                return handleChildProc(parsedArgs, descriptors, childPipeFd,                        parsedArgs.startChildZygote);            } else {                // In the parent. A pID < 0 indicates a failure and will be handled in                // handleParentProc.                //父进程执行                IoUtils.closeQuIEtly(childPipeFd);                childPipeFd = null;                handleParentProc(pID, descriptors, serverPipeFd);                return null;            }        } finally {            IoUtils.closeQuIEtly(childPipeFd);            IoUtils.closeQuIEtly(serverPipeFd);        }}

  从上面的代码可以看出,Zygote采用高效的I/O多路复用机制,保证在没有客户端连接请求或数据处理时休眠,否则响应客户端的请求。


2.6 ZygoteConnection.processOneCommand

  该代码定义在frameworks/base/core/java/com/androID/internal/os/ZygoteConnection.java中,解析socket客户端即AMS传递过来的参数,然后调用forkAndSpecialize创建App进程。



总结

  到这里zygote启动就基本告一段落了,zygote启动的调用流程图如下所示:


细数下来,zygote进程启动主要干了如下的相关大事:

解析init.zygotexxx.rc传递过来的参数,创建AppRuntime并调用AppRuntime.start()方法;调用AndroIDRuntime的startVM()方法创建虚拟机,再调用startReg()注册JNI函数虚拟机和JNI环境构建后以后,通过JNI方式调用ZygoteInit.main(),正式进入的Java世界调用registerServerSocketFromEnv()建立socket通道,zygote作为通信的服务端,用于响应客户端请求,这里大伙可以思考一个问题就是为啥用的是zygote通信而不是binder preload()预加载通用类、drawable和color资源、openGL以及共享库以及WebVIEw,用于提高app启动效率zygote完毕大部分工作,接下来再通过forkSystemServer(),fork得力帮手system_server进程,也是上层framework的运行载体zygote功成身退,调用runSelectLoop(),随时待命,当接收到请求创建新进程请求时立即唤醒并执行相应工作。

写在最后

  AndroID 9 zygote启动就告一段落了,但是这个只是一个开端,因为其中的forkSystemServer启动system_server和forkAndSpecialize启动App并没有讲解,这个牵涉的东西太多了,现在功力不足以将其写清楚,所以接下来会修炼内功,在接下来的篇章中力求讲上述两个遗留的问题说透彻。如果对给位有帮助欢迎点赞一个,如果写得有问题也欢迎多多指正。未完待续,下个篇章再见。

总结

以上是内存溢出为你收集整理的Android 9 (P) Zygote进程启动源码分析指南二全部内容,希望文章能够帮你解决Android 9 (P) Zygote进程启动源码分析指南二所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1058659.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-25
下一篇 2022-05-25

发表评论

登录后才能评论

评论列表(0条)

保存