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


Android 9 (P) 系统启动及进程创建源码分析目录:

Android 9 (P)之init进程启动源码分析指南之一
Android 9 (P)之init进程启动源码分析指南之二
Android 9 (P)之init进程启动源码分析指南之三
Android 9 (P)核心服务和关键进程启动
Android 9 (P)Zygote进程启动源码分析指南一
Android 9 (P)Zygote进程启动源码分析指南二
Android 9 (P)系统启动之SystemServer大揭秘上
Android 9 (P)系统启动之SystemServer大揭秘下
Android 9 (P)应用进程创建流程大揭秘



引言

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

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

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

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

frameworks/base/include/android_runtime/AndroidRuntime.h
frameworks/base/core/jni/AndroidRuntime.cpp
frameworks/base//core/java/com/android/internal/os/ZygoteInit.java
frameworks/base/core/java/com/android/internal/os/ZygoteServer.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 != '\0'; 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

  该代码定义在frameworks/base//core/java/com/android/internal/os/ZygoteInit.java中,这里不过多详细解释,在main方法中主要做了如下几件事情:

  • 解析参数
  • 调用zygoteServer.registerServerSocketFromEnv创建zygote通信的服务端
  • 调用forkSystemServer启动system_server
  • zygoteServer.runSelectLoop进入循环模式
	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);//进入循环模式
	......
}

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.。

    /**
     * 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);
            }
        }
    }

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

  我们知道ActivityManagerService 通过 Process.start 来创建新的进程,而 Process.start 会先通过socket 连接到 zygote 进程,并最终由 zygote 完成进程创建。如下是App进程创建请求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);
    }                                               

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

  • zygote比service manager先启动,从这个意义触发,zygote没有service manager可以注册binder,所以没有办法binder
  • zygote进程和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 的耗时。

//预加载位于/system/etc/preloaded-classes文件中的类
    preloadClasses();

    //预加载资源,包含drawable和color资源
    preloadResources();

    //预加载OpenGL
    preloadOpenGL();

    //通过System.loadLibrary()方法,
    //预加载"android","compiler_rt","jnigraphics"这3个共享库
    preloadSharedLibraries();

    //预加载 文本连接符资源
    preloadTextResources();

    //仅用于zygote进程,用于内存共享的进程
    WebViewFactory.prepareWebViewInZygote();
  • preloadClasses()预加载的文件路径为/system/etc/preloaded-classes,预加载比较多有大概4000多个类,这里对于类的加载主要通过Class.forName()方法来进行的。
[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;

  • 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加载到内存的。
06-04 15:39:01.041  2901  2901 D Zygote  : begin preload
06-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.ttf
06-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_MtpDevice
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.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/overlay
06-04 15:39:01.352  2901  2901 W Zygote  : Class not found for preloading: android.view.Display$ColorTransform
06-04 15:39:01.352  2901  2901 W Zygote  : Class not found for preloading: android.view.Display$ColorTransform$1
06-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 starting
06-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=40345
06-04 15:39:01.665  2901  2901 I art     : VMRuntime.preloadDexCaches types total=24111 before=8040 after=8075
06-04 15:39:01.665  2901  2901 I art     : VMRuntime.preloadDexCaches fields total=115222 before=41429 after=41660
06-04 15:39:01.665  2901  2901 I art     : VMRuntime.preloadDexCaches methods total=201879 before=83038 after=83782
06-04 15:39:01.665  2901  2901 I art     : VMRuntime.preloadDexCaches finished
06-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.so
06-04 15:39:01.773  2901  2901 D libEGL  : loaded /vendor/lib64/egl/libGLESv1_CM_adreno.so
06-04 15:39:01.785  2901  2901 D libEGL  : loaded /vendor/lib64/egl/libGLESv2_adreno.so
06-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 preload
06-04 15:39:01.831  2901  2901 I art     : Starting a blocking GC Explicit
06-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.977ms
06-04 15:39:01.858  2901  2901 I art     : Starting a blocking GC Explicit
06-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.445ms
06-04 15:39:01.873  2901  2908 I art     : Starting a blocking GC HeapTrim
06-04 15:39:01.876  2901  2901 I art     : Starting a blocking GC Background

  当经过上述的步骤后,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

  该代码定义在frameworks/base//core/java/com/android/internal/os/ZygoteInit.java,其逻辑主要就是准备参数,并启动 system_server 进程,后续启动的Android Java 系统服务都将驻留在该进程中,它是是Android framework核心。这里主要设置了system_server 进程uid和gid,process name,class name。并且从zygote进程fork新进程后,需要关闭zygote原有的socket,另外,对于有两个zygote进程情况,需等待第2个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;
    }
06-04 15:39:01.920  2901  2901 I Zygote  : System server process 2910 has been created
06-04 15:39:01.923  2901  2901 I Zygote  : Accepting command socket connections
06-04 15:39:01.940  2910  2910 I Zygote  : Process: zygote socket opened, supported ABIS: armeabi-v7a,armeabi
06-04 15:39:01.941  2910  2910 I InstallerConnection: connecting...
06-04 15:39:01.942   771   771 I         : new connection
06-04 15:39:01.945  2910  2910 I InstallerConnection: disconnecting...
06-04 15:39:01.945   771   771 E         : eof
06-04 15:39:01.945   771   771 E         : failed to read size
06-04 15:39:01.945   771   771 I         : closing connection
06-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!

  这里我有一个疑问就是设置的system_server进程的uid=1000,gid=1000可是实际ps查看的并不是,这个希望大伙一起探讨。各位不好意思误导大伙了,这里的USER为sytem就代表uid为1000,这里把PID和UID混为一谈了。

127|msm8953_64:/ # ps  1515
USER      PID   PPID  VSIZE  RSS   WCHAN              PC  NAME
system    1515  762   2384060 133700 SyS_epoll_ 0000000000 S system_server

2.5 ZygoteServer.runSelectLoop

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

    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) {
        	//每次循环,都重新创建需要监听的pollFds
            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);
            }
			/*注意这里是倒序处理的,网上有的博客说是优先处理已建立连接的信息,后处理新建连接的请求
			* 我觉得这个表述不是很正确,我觉得采用倒序是为了先处理已经建立连接的请求,但是这个优先反而是后面建立连接的请求有数据到来是优先处理了
			* 然后接着最后处理sServerSocket,此时即有新的客户端要求建立连接
			*/
            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和fds,下一次也开始监听
                    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;
                    }
                }
            }
        }
    }                 

  从上面的代码可以看出,Zygote采用高效的I/O多路复用机制,保证在没有客户端连接请求或数据处理时休眠,否则响应客户端的请求。从前面可以看到初始时fds中仅有server socket,因此当有数据到来时,将执行i等于0的分支。此时,显然是需要创建新的通信连接,因此acceptCommandPeer将被调用。让我们接着分析看看它究竟干了些啥!


2.6 ZygoteServer.acceptCommandPeer

  让我们接着分析这段代码,如下:

    private ZygoteConnection acceptCommandPeer(String abiList) {
        try {
            // socket编程中,accept()调用主要用在基于连接的套接字类型,比如SOCK_STREAM和SOCK_SEQPACKET
            // 它提取出所监听套接字的等待连接队列中第一个连接请求,创建一个新的套接字,并返回指向该套接字的文件描述符
            // 新建立的套接字不在监听状态,原来所监听的套接字的状态也不受accept()调用的影响,这个就是套接字编程的基础了
            return createNewConnection(mServerSocket.accept(), abiList);
        } catch (IOException ex) {
            throw new RuntimeException(
                    "IOException during accept()", ex);
        }
    }

    protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
            throws IOException {
        return new ZygoteConnection(socket, abiList);
    }

  通过上面的代码我们可以看到,acceptCommandPeer主要是基础的socket套接字编程,调用了server socket的accpet函数等待客户端的连接。当有新的连接建立时,zygote进程将会创建出一个新的socket与其通信,并将该socket加入到fds中。所以一旦和客户端进程的通信连接建立后,fds中将会有多个socket至少会有两个。

  当poll监听到这一组sockets上有数据到来时,就会从阻塞中恢复。于是,我们需要判断到底是哪个socket收到了数据。


2.7 ZygoteConnection.processOneCommand

  该代码定义在frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java中,解析socket客户端即AMS传递过来的参数,然后调用forkAndSpecialize创建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);
        }
}

  好了到此处runSelectLoop已经讲解完毕了,我们可以看出它采用的是倒序的方式进行轮询。且由于server socket第一个被加入到fds,所以它是最后被轮询到的。因此最后轮询到的socket才需要处理新建连接的操作;其它socket收到数据时,仅需要调用zygoteConnection的processOneCommand函数执行数据对应的操作。若一个连接处理完所有对应消息后,该连接对应的socket和连接等将被移除。




总结

  到这里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并没有讲解,这个牵涉的东西太多了,现在功力不足以将其写清楚,所以接下来会修炼内功,在接下来的篇章中力求讲上述两个遗留的问题说透彻。如果对给位有帮助欢迎点赞一个,如果写得有问题也欢迎多多指正。未完待续,下个篇章再见。好了system_server启动的博客终于来了,参见Android 9 (P)系统启动之SystemServer大揭秘上篇章!

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐