前言
各位老司机们,现在闲下来终于有时间接着续写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又是多线程,为了避免这些麻烦所以干脆使用socket2.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进程启动源码分析指南二所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)