- registerServerSocketFromEnv registers the server socket for cross process communication. Binder communication is not used here.
- preload() to perform the preload operation
- gcAndFinalize(), take the initiative to conduct a garbage collection before forkSystemServer
- forkSystemServer(), create SystemServer process
- runSelectLoop(), waiting for the socket request from the client
The above is basically the whole mission of Zygote. The following is a detailed analysis according to this process.
registerServerSocketFromEnv
> ZygoteServer.java void registerServerSocketFromEnv(String socketName) { if (mServerSocket == null) { int fileDesc; final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName; try { // Get fd of socket from environment variable 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); // Set file descriptor mServerSocket = new LocalServerSocket(fd); // Create a server socket mCloseSocketFd = true; } catch (IOException ex) { throw new RuntimeException( "Error binding to local socket '" + fileDesc + "'", ex); } } }
First, get the file descriptor fd of the socket from the environment variable, and then create the server LocalServerSocket according to fd for IPC communication. The environment variable here is set when the init process creates the Zygote process.
preload()
> ZygoteInit.java static void preload(TimingsTraceLog bootTimingsTraceLog) { ...... preloadClasses(); // Preload and initialize the classes in / system / etc / preloaded classes ...... preloadResources(); // Preload system resources ...... nativePreloadAppProcessHALs(); // HAL? ...... preloadOpenGL(); // Preload OpenGL ...... preloadSharedLibraries(); // Preload shared libraries, including android and compiler_rt and JNI graphics preloadTextResources(); // Preload text resources // Ask the WebViewFactory to do any initialization that must run in the zygote process, // for memory sharing purposes. // Some initialization work that must be done in the zygote process in WebViewFactory is used for shared memory WebViewFactory.prepareWebViewInZygote(); warmUpJcaProviders(); sPreloadComplete = true; }
The preload() method mainly preloads some classes, resources and shared libraries to improve runtime efficiency. Let's take a look at what is preloaded in turn.
preloadClasses()
> ZygoteInit.java private static void preloadClasses() { ...... InputStream is; try { // /system/etc/preloaded-classes is = new FileInputStream(PRELOADED_CLASSES); } catch (FileNotFoundException e) { Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + "."); return; } try { BufferedReader br = new BufferedReader(new InputStreamReader(is), 256); int count = 0; String line; while ((line = br.readLine()) != null) { // Skip comments and blank lines. line = line.trim(); if (line.startsWith("#") || line.equals("")) { continue; } try { // Load and explicitly initialize the given class. Use // Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups // (to derive the caller's class-loader). Use true to force initialization, and // null for the boot classpath class-loader (could as well cache the // class-loader of this class in a variable). Class.forName(line, true, null); count++; } catch (ClassNotFoundException e) { Log.w(TAG, "Class not found for preloading: " + line); } catch (UnsatisfiedLinkError e) { Log.w(TAG, "Problem preloading " + line + ": " + e); } catch (Throwable t) { ...... } } } catch (IOException e) { Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e); } finally { IoUtils.closeQuietly(is); ...... } }
Only the core logic code is retained. Read the / system / etc / preloaded classes file and load the classes declared in the file line by line through the Class.forName() method. Preloading commonly used classes in the system in advance can undoubtedly improve runtime efficiency, but the work of preloading commonly used classes is usually very heavy. Search the entire source code library and find a preloaded classes file in the / frameworks/base/config directory. Open this file with a total of 6558 lines, which means loading thousands of classes in advance, which will undoubtedly take a long time to increase the startup time of the Android system and improve the efficiency of the runtime.
preloadResources()
> ZygoteInit.java private static void preloadResources() { final VMRuntime runtime = VMRuntime.getRuntime(); try { mResources = Resources.getSystem(); mResources.startPreloading(); if (PRELOAD_RESOURCES) { TypedArray ar = mResources.obtainTypedArray( com.android.internal.R.array.preloaded_drawables); int N = preloadDrawables(ar); ar.recycle(); ...... ar = mResources.obtainTypedArray( com.android.internal.R.array.preloaded_color_state_lists); N = preloadColorStateLists(ar); ar.recycle(); if (mResources.getBoolean( com.android.internal.R.bool.config_freeformWindowManagement)) { ar = mResources.obtainTypedArray( com.android.internal.R.array.preloaded_freeform_multi_window_drawables); N = preloadDrawables(ar); ar.recycle(); } } mResources.finishPreloading(); } catch (RuntimeException e) { Log.w(TAG, "Failure preloading resources", e); } }
It can be seen from the source code that the main loaded resources are:
com.android.internal.R.array.preloaded_drawables
com.android.internal.R.array.preloaded_color_state_lists
com.android.internal.R.array.preloaded_freeform_multi_window_drawables
preloadSharedLibraries()
> ZygoteInit.java private static void preloadSharedLibraries() { Log.i(TAG, "Preloading shared libraries..."); System.loadLibrary("android"); System.loadLibrary("compiler_rt"); System.loadLibrary("jnigraphics"); }
Three shared libraries, libandroid.so and libcompiler, are preloaded_ Rt.so and libjnigraphics.so.
gcAndFinalize()
> ZygoteInit.java static void gcAndFinalize() { final VMRuntime runtime = VMRuntime.getRuntime(); /* runFinalizationSync() lets finalizers be called in Zygote, * which doesn't have a HeapWorker thread. */ System.gc(); runtime.runFinalizationSync(); System.gc(); }
Before forkSystemServer(), a GC operation will be initiated.
forkSystemServer()
After actively calling GC, Zygote will do its big thing - fork SystemServer process.
> ZygoteInit.java private static Runnable forkSystemServer(String abiList, String socketName, ...... /* Hardcoded command line to start the system server */ // startup parameter 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", // Process name "--runtime-args", "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT, "com.android.server.SystemServer", // Load class name }; 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 process */ 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 */ // pid == 0 indicates that the child process enters the system from here_ Server process if (pid == 0) { if (hasSecondZygote(abiList)) { // If there is a second Zygote waitForSecondaryZygote(socketName); } zygoteServer.closeServerSocket(); // Close and release the socket from Zygote copy return handleSystemServerProcess(parsedArgs); // Complete the newly created system_ Remaining work of the server process } /** * Note that the fork() function executes once and returns twice (two executions of the same program by two processes). * pid > 0 Description is also the parent process. pid = 0 indicates that a child process has been entered * So return null here will still be executed */ return null; }
As can be seen from the above startup parameters, the uid and gid of the SystemServer process are both 1000, and the process name is system_server. The last class name to be loaded is com.android.server.SystemServer. After preparing a series of parameters, splice them through ZygoteConnection.Arguments(), and then call the Zygote.forkSystemServer() method to actually fork out the child process system_server.
> Zygote.java public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) { VM_HOOKS.preFork(); // Resets nice priority for zygote process. resetNicePriority(); int pid = nativeForkSystemServer( uid, gid, gids, runtimeFlags, rlimits, permittedCapabilities, effectiveCapabilities); // Enable tracing as soon as we enter the system_server. if (pid == 0) { Trace.setTracingEnabled(true, runtimeFlags); } VM_HOOKS.postForkCommon(); return pid; } native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
The last fork () operation is done in the native layer. Go back to ZygoteInit.forkSystemServer() and perform the logical processing after fork():
if(pid == 0){ ...... return handleSystemServerProcess(parsedArgs); } return null;
According to normal logic, these two returns will only be executed once. In fact, they are not. The fork() function is executed once and returned twice. To be more precise, two processes execute a program twice. When pid == 0, it indicates that it is in the child process. When PID > 0, it indicates that it is in the parent process. When the child process was first forked out, the data structure of the parent and child processes was basically the same, but then they parted ways and implemented their own logic. Therefore, there will be two return values in the above code segment, the sub process (system_server) will return the result of executing handleSystemServerProcess(parsedArgs), and the parent process (zygote) will return null. What will be done for two different return values? Let's go back to ZygoteInit.main():
if (startSystemServer) { Runnable r = forkSystemServer(abiList, socketName, zygoteServer); // {@code r == null} in the parent (zygote) process, and {@code r != null} in the // child (system_server) process. // r == null indicates that it is in the zygote process // r != null description is in system_server process if (r != null) { r.run(); return; } } // Loop waiting to process client requests caller = zygoteServer.runSelectLoop(abiList);
Subprocess system_ The server returns a Runnable. Execute r.run() and return directly. The parent process zygote returns null, so it does not meet the judgment conditions of if. Continue to run runSelectLoop. The father and son process parted ways and did their own things.
Let's analyze the runSelectLoop() and handleSystemServerProcess() methods to see what the parent-child processes Zygote and SystemServer continue to do.
handleSystemServerProcess
In fact, it's out of the scope of Zygote. I'm going to introduce it in the next system server source code analysis, but I don't write it here and feel that the introduction of Zygote is incomplete, so I'll just say it together.
> ZygoteInit.java private static Runnable handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) { // set umask to 0077 so new files and directories will default to owner-only permissions. // umask is generally used to give permissions to a directory or file when you initially create it Os.umask(S_IRWXG | S_IRWXO); // Set the current process name to "system"_ server" if (parsedArgs.niceName != null) { Process.setArgV0(parsedArgs.niceName); } final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH"); if (systemServerClasspath != null) { // dex optimization operation performSystemServerDexOpt(systemServerClasspath); // Capturing profiles is only supported for debug or eng builds since selinux normally // prevents it. boolean profileSystemServer = SystemProperties.getBoolean( "dalvik.vm.profilesystemserver", false); if (profileSystemServer && (Build.IS_USERDEBUG || Build.IS_ENG)) { try { prepareSystemServerProfile(systemServerClasspath); } catch (Exception e) { Log.wtf(TAG, "Failed to set up system server profile", e); } } } if (parsedArgs.invokeWith != null) { // invokeWith is generally empty String[] args = parsedArgs.remainingArgs; // If we have a non-null system server class path, we'll have to duplicate the // existing arguments and append the classpath to it. ART will handle the classpath // correctly when we exec a new process. if (systemServerClasspath != null) { String[] amendedArgs = new String[args.length + 2]; amendedArgs[0] = "-cp"; amendedArgs[1] = systemServerClasspath; System.arraycopy(args, 0, amendedArgs, 2, args.length); args = amendedArgs; } WrapperInit.execApplication(parsedArgs.invokeWith, parsedArgs.niceName, parsedArgs.targetSdkVersion, VMRuntime.getCurrentInstructionSet(), null, args); throw new IllegalStateException("Unexpected return from WrapperInit.execApplication"); } else { ClassLoader cl = null; if (systemServerClasspath != null) { // Create a class loader and assign it to the current thread cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion); Thread.currentThread().setContextClassLoader(cl); } /* * Pass the remaining arguments to SystemServer. */ return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl); } /* should never reach here */ }
Set the process name to system_server, performs dex optimization, sets the class loader for the current thread, and finally calls ZygoteInit.zygoteInit() to continue processing the remaining parameters.
public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) { ...... // Redirect System.out and System.err to the Android log. // Redirect System.out and System.err to Android log RuntimeInit.redirectLogStreams(); RuntimeInit.commonInit(); // Some initialization work ZygoteInit.nativeZygoteInit(); // native layer initialization return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader); // Call entry function }
Redirect the Log and perform some initialization. I won't elaborate on this part. Click the source code link given at the beginning of the article, and most of them have been annotated. Finally, call RuntimeInit.applicationInit() and continue to look inside.
> RuntimeInit.java protected static Runnable applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) { ...... final Arguments args = new Arguments(argv); // Analytical parameters ...... // Find the main() method of startClass. The startClass here is com.android.server.SystemServer return findStaticMain(args.startClass, args.startArgs, classLoader); }
The startClass parameter here is com.android.server.SystemServer. The findStaticMain() method can be known by name. Its function is to find the main() function. Here is to find the main() method of com.android.server.SystemServer class.
protected static Runnable findStaticMain(String className, String[] argv, ClassLoader classLoader) { Class<?> cl; try { cl = Class.forName(className, true, classLoader); } catch (ClassNotFoundException ex) { throw new RuntimeException( "Missing class when invoking static main " + className, ex); } Method m; try { // Find the main() method m = cl.getMethod("main", new Class[] { String[].class }); } catch (NoSuchMethodException ex) {