Android framework(二):Zygote进程

    技术2022-07-10  140

    一、概述

    zygote,在英语中是受精卵的意思。而在Android系统中也有这么一个“受精卵进程” – Zygote进程。

    在Android系统中,Zygote进程是所有Android进程的父进程。它通过fork的形式,创建SystemServer进程和应用程序进程。而Zygote进程则是通过linux系统的init进程启动的。

    在Android系统中各种进程的启动过程:init进程 ––> Zygote进程 ––> SystemServer进程 ––>各种应用进程

    其中,应用程序进程即我们编写的应用的进程,SystemServer进程为系统服务进程,比如后面还要学到的AMS、WMS都是在该进程中。

    需要注意的是:

    1、Android中所有的应用进程的创建都是一个应用进程通过Binder请求SystemServer进程,SystemServer进程发送socket消息给Zygote进程,统一由Zygote进程创建出来的。

    2、Zygote进程在启动的时候会创建一个虚拟机实例,因此通过Zygote进程在父进程,创建的子进程都会继承这个虚拟机实例,App中的JAVA代码可以得到翻译执行。

    3、进程与进程之间需要跨进程通信,由Zygote进程作为父进程还可以获得一个Binder线程池,这样进程之间就可以使用Binder进行跨进程通信了。

    二、启动过程

    /frameworks/base/cmds/app_process/app_main.cpp

    192int main(int argc, char* const argv[]) 193{ ... 282 // Parse runtime arguments. Stop at first unrecognized option. 283 bool zygote = false; 284 bool startSystemServer = false; 285 bool application = false; 286 String8 niceName; 287 String8 className; 288 289 ++i; // Skip unused "parent dir" argument. //init.rc中会配置一些参数,这里进行比较设置一些变量走进不同的分支 290 while (i < argc) { 291 const char* arg = argv[i++]; 292 if (strcmp(arg, "--zygote") == 0) { //启动的是Zygote进程 293 zygote = true; 294 niceName = ZYGOTE_NICE_NAME; 295 } else if (strcmp(arg, "--start-system-server") == 0) { //启动的是system-server进程 296 startSystemServer = true; 297 } else if (strcmp(arg, "--application") == 0) { 298 application = true; 299 } else if (strncmp(arg, "--nice-name=", 12) == 0) { 300 niceName.setTo(arg + 12); 301 } else if (strncmp(arg, "--", 2) != 0) { 302 className.setTo(arg); 303 break; 304 } else { 305 --i; 306 break; 307 } 308 } 309 ... //设置一个“好听的名字” zygote,之前的名称是app_process 357 if (!niceName.isEmpty()) { 358 runtime.setArgv0(niceName.string(), true /* setProcName */); 359 } 360 361 if (zygote) { //通过runtime启动zygote 364 runtime.start("com.android.internal.os.ZygoteInit", args, zygote); 365 } else if (className) { 366 runtime.start("com.android.internal.os.RuntimeInit", args, zygote); 367 } else { 368 fprintf(stderr, "Error: no class name or --zygote supplied.\n"); 369 app_usage(); 370 LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); 371 } 372}

    /frameworks/base/core/jni/AndroidRuntime.cpp

    void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) 1057{ 1058 ALOGD(">>>>>> START %s uid %d <<<<<<\n", 1059 className != NULL ? className : "(unknown)", getuid()); 1060 1061 static const String8 startSystemServer("start-system-server"); 1062 1063 /* 1064 * 'startSystemServer == true' means runtime is obsolete and not run from 1065 * init.rc anymore, so we print out the boot start event here. 1066 */ 1067 for (size_t i = 0; i < options.size(); ++i) { 1068 if (options[i] == startSystemServer) { 1069 /* track our progress through the boot sequence */ 1070 const int LOG_BOOT_PROGRESS_START = 3000; 1071 LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); 1072 } 1073 } 1074 1075 const char* rootDir = getenv("ANDROID_ROOT"); 1076 if (rootDir == NULL) { 1077 rootDir = "/system"; 1078 if (!hasDir("/system")) { 1079 LOG_FATAL("No root directory specified, and /android does not exist."); 1080 return; 1081 } 1082 setenv("ANDROID_ROOT", rootDir, 1); 1083 } 1084 1085 //const char* kernelHack = getenv("LD_ASSUME_KERNEL"); 1086 //ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack); 1087 1088 /* start the virtual machine */ 1089 JniInvocation jni_invocation; 1090 jni_invocation.Init(NULL); 1091 JNIEnv* env; 1092 if (startVm(&mJavaVM, &env, zygote) != 0) { 1093 return; 1094 } 1095 onVmCreated(env); 1096 1097 /* 1098 * Register android functions. 1099 */ 1100 if (startReg(env) < 0) { 1101 ALOGE("Unable to register all android natives\n"); 1102 return; 1103 } 1104 1105 /* 1106 * We want to call main() with a String array with arguments in it. 1107 * At present we have two arguments, the class name and an option string. 1108 * Create an array to hold them. 1109 */ 1110 jclass stringClass; 1111 jobjectArray strArray; 1112 jstring classNameStr; 1113 1114 stringClass = env->FindClass("java/lang/String"); 1115 assert(stringClass != NULL); 1116 strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL); 1117 assert(strArray != NULL); 1118 classNameStr = env->NewStringUTF(className); 1119 assert(classNameStr != NULL); 1120 env->SetObjectArrayElement(strArray, 0, classNameStr); 1121 1122 for (size_t i = 0; i < options.size(); ++i) { 1123 jstring optionsStr = env->NewStringUTF(options.itemAt(i).string()); 1124 assert(optionsStr != NULL); 1125 env->SetObjectArrayElement(strArray, i + 1, optionsStr); 1126 } 1127 1128 /* 1129 * Start VM. This thread becomes the main thread of the VM, and will 1130 * not return until the VM exits. 1131 */ 1132 char* slashClassName = toSlashClassName(className != NULL ? className : ""); 1133 jclass startClass = env->FindClass(slashClassName); 1134 if (startClass == NULL) { 1135 ALOGE("JavaVM unable to locate class '%s'\n", slashClassName); 1136 /* keep going */ 1137 } else { 1138 jmethodID startMeth = env->GetStaticMethodID(startClass, "main", 1139 "([Ljava/lang/String;)V"); 1140 if (startMeth == NULL) { 1141 ALOGE("JavaVM unable to find main() in '%s'\n", className); 1142 /* keep going */ 1143 } else { 1144 env->CallStaticVoidMethod(startClass, startMeth, strArray); 1145 1146#if 0 1147 if (env->ExceptionCheck()) 1148 threadExitUncaughtException(env); 1149#endif 1150 } 1151 } 1152 free(slashClassName); 1153 //这行Log比较常见,因为其他应用进程也是由zygote 进程fork 出来的,所有其他进程也包含这段代码,如果其他进程在java 层crash,那么也会走到这里 1154 ALOGD("Shutting down VM\n"); 1155 if (mJavaVM->DetachCurrentThread() != JNI_OK) 1156 ALOGW("Warning: unable to detach main thread\n"); 1157 if (mJavaVM->DestroyJavaVM() != 0) 1158 ALOGW("Warning: VM did not shut down cleanly\n"); 1159}

    主要做了三件事情,1、调用startVm开启虚拟机,2、调用startReg注册JNI方法,3、使用JNI把Zygote进程启动起来。启动的类名为 com.android.internal.os.ZygoteInit,方法名为main。

    frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

    public static void main(String argv[]) { //1、创建ZygoteServer ZygoteServer zygoteServer = new ZygoteServer(); //此处开启设置,创建过程禁止多线程,为什么?后面会解答 ZygoteHooks.startZygoteNoThreadCreation(); ... try { ... //设置DDMS可用 RuntimeInit.enableDdms(); //初始化启动参数 boolean startSystemServer = false; String socketName = "zygote"; String abiList = null; boolean enableLazyPreload = false; // 2、解析app_main.cpp传来的参数 for (int i = 1; i < argv.length; i++) { if ("start-system-server".equals(argv[i])) { startSystemServer = true; } else if ("--enable-lazy-preload".equals(argv[i])) { enableLazyPreload = true; } else if (argv[i].startsWith(ABI_LIST_ARG)) { abiList = argv[i].substring(ABI_LIST_ARG.length()); } else if (argv[i].startsWith(SOCKET_NAME_ARG)) { socketName = argv[i].substring(SOCKET_NAME_ARG.length()); } else { throw new RuntimeException("Unknown command line argument: " + argv[i]); } } if (abiList == null) { throw new RuntimeException("No ABI list supplied."); } //3、创建一个Server端的Socket zygoteServer.registerServerSocketFromEnv(socketName); // In some configurations, we avoid preloading resources and classes eagerly. // In such cases, we will preload things prior to our first fork. if (!enableLazyPreload) { bootTimingsTraceLog.traceBegin("ZygotePreload"); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); //4、预加载进程的资源和类 preload(bootTimingsTraceLog); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis()); bootTimingsTraceLog.traceEnd(); // ZygotePreload } else { Zygote.resetNicePriority(); } ... //此处关闭设置,允许多线程 ZygoteHooks.stopZygoteNoThreadCreation(); //5、fork一个SystemServer进程 if (startSystemServer) { Runnable r = forkSystemServer(abiList, socketName, zygoteServer); if (r != null) { r.run(); return; } } //6、启动一个死循环,监听socket,启动新的应用进程 caller = zygoteServer.runSelectLoop(abiList); } catch (Throwable ex) { Log.e(TAG, "System zygote died with exception", ex); zygoteServer.closeServerSocket(); throw ex; } finally { zygoteServer.closeServerSocket(); } if (caller != null) { caller.run(); } }

    总结一下,在main方法中主要完成了6件事:

    创建ZygoteServer解析app_main.cpp传来的参数,获取abi列表,获取socket连接名称。(这里需要注意的是:android系统中进程之间通讯的方式是Binder,但是有一个例外是SystemService进程与Zygote进程之间是通过Socket的方式进行通讯的)通过registerZygoteSocket函数,注册Socket,作为服务端,接受ActivityManagerService的请求来创建应用程序进程预加载类和资源,包括颜色,R文件,drawable、类等启动system_server进程,SystemServer进程会启动系统的关键服务调用runSelectLoop函数监听socket来等待客户端请求

    /frameworks/base/core/java/com/android/internal/os/ZygoteServer.java

    void registerServerSocketFromEnv(String socketName) { if (mServerSocket == null) { int fileDesc; final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName; try { //从环境变量中获取名为ANDROID_SOCKET_zygote的fd String env = System.getenv(fullSocketName); fileDesc = Integer.parseInt(env); } catch (RuntimeException ex) { throw new RuntimeException(fullSocketName + " unset or invalid", ex); } try { //创建一个FD对象 FileDescriptor fd = new FileDescriptor(); fd.setInt$(fileDesc); //不是使用IP和端口,这里用这个FD创建LocalServerSocket mServerSocket = new LocalServerSocket(fd); mCloseSocketFd = true; } catch (IOException ex) { throw new RuntimeException( "Error binding to local socket '" + fileDesc + "'", ex); } } }

    注意,这时通过fd创建了一个socket服务端–LocalServerSocket。当Zygote进程将SystemServer进程启动后,就会在这个服务端的Socket上来等待ActivityManagerService请求Zygote进程来创建新的应用程序进程。

    这里为什么是使用FD创建呢?LocalServerSocket具体是怎么创建的?

    具体可以查看 -> https://www.jianshu.com/p/ab9b83a77af6

    frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

    static void preload(TimingsTraceLog bootTimingsTraceLog) { 124 Log.d(TAG, "begin preload"); 125 bootTimingsTraceLog.traceBegin("BeginIcuCachePinning"); 126 beginIcuCachePinning(); 127 bootTimingsTraceLog.traceEnd(); // BeginIcuCachePinning 128 bootTimingsTraceLog.traceBegin("PreloadClasses"); //预加载类,在preloadClasses方法中会通过Class.forName将类加载到系统中,生成字节码 129 preloadClasses(); 130 bootTimingsTraceLog.traceEnd(); // PreloadClasses 131 bootTimingsTraceLog.traceBegin("PreloadResources"); //预加载资源文件 132 preloadResources(); 133 bootTimingsTraceLog.traceEnd(); // PreloadResources 134 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadAppProcessHALs"); 135 nativePreloadAppProcessHALs(); 136 Trace.traceEnd(Trace.TRACE_TAG_DALVIK); 137 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL"); 138 preloadOpenGL(); 139 Trace.traceEnd(Trace.TRACE_TAG_DALVIK); //预加载共享库 140 preloadSharedLibraries(); //预加载文字资源 141 preloadTextResources(); 142 // Ask the WebViewFactory to do any initialization that must run in the zygote process, 143 // for memory sharing purposes. 144 WebViewFactory.prepareWebViewInZygote(); 145 endIcuCachePinning(); 146 warmUpJcaProviders(); 147 Log.d(TAG, "end preload"); 148 149 sPreloadComplete = true; 150 }

    从代码中,我们可以看到哪些资源会被进行预加载:类文件、资源文件、共享库、文字资源。总的来说,这些都是系统中App共享的资源。通过此时预加载,可以减少资源加载时间,加快了应用启动速度。

    在preloadClasses()函数中,会通过Class.forName将类加载到系统中,生成字节码。此时预加载的类有哪些,可以查看清单:http://androidxref.com/8.0.0_r4/xref/frameworks/base/preloaded-classes

    注意:这里预加载资源都是在主线程中完成的,为什么不开多线程呢?猜测是防止多线程带来的同步问题。所以才在ZygoteInit的方法中开启了禁止多线程的设置,完成后才关闭。

    Processed: 0.039, SQL: 9