Android source code analysis directory
(Note: The following code analysis is based on Android-10.0.0_R30)
An overview of
In this article, we will discuss how Zygote works
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
frameworks/base/core/java/android/net/LocalServerSocket.java
frameworks/base/core/java/android/net/LocalSocket.java
frameworks/base/core/java/com/android/internal/os/Zygote.java
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
Copy the code
Two ZygoteInit
The entry to Zygote’s work is opened by a few lines of code in ZygoteInit
[ZygoteInit.java]
Runnable caller; ZygoteServer = new ZygoteServer(isPrimaryZygote); Pass zygoteServer / / get a run object caller = zygoteServer. RunSelectLoop (abiList); . if (caller ! = null) {// start loop caller.run(); }Copy the code
Three ZygoteServer. Java
3.1 ZygoteServer constructor
[ZygoteServer.java]
ZygoteServer(Boolean isPrimaryZygote) {UsapPoolEventFD, The fd is to use ZygoteServer polling mUsapPoolEventFD = Zygote. GetUsapPoolEventFD (); If (isPrimaryZygote) {// Create mZygoteSocket, The ZygoteSocket is actually LocalServerSocket and inside LocalServerSocket there's an implementation class LocalSocketImpl, In this creation method will create a fd / / after polling is used inside the fd mZygoteSocket = Zygote. CreateManagedSocketFromInitSocket (Zygote. PRIMARY_SOCKET_NAME); / / create a UsapPoolSocket mUsapPoolSocket = Zygote. CreateManagedSocketFromInitSocket (Zygote. USAP_POOL_PRIMARY_SOCKET_NAME); } else { mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME); mUsapPoolSocket = Zygote.createManagedSocketFromInitSocket( Zygote.USAP_POOL_SECONDARY_SOCKET_NAME); } fetchUsapPoolPolicyProps(); mUsapPoolSupported = true; }Copy the code
In the ZygoteServer constructor, a ZygoteSocket is created, which is actually a LocalServerSocket. This class has a corresponding implementation class LocalSocketImpl and a fd, because in Android, The communication between Zygote and AMS uses sock rather than binder, and socket is based on C/S architecture. Therefore, ZygoteServer, as the Server, has a LocalServerSocket. If the Client wants to communicate, Then the corresponding LocalSocket on the Client side needs to be used
3.2 runSelectLoop
The runSelectLoop method is a long one. Although it is used to process the socket messages of the Client, UsapPool is introduced, so there is a lot of UsapPool logic. However, the main processing logic in runSelectLoop is: It’s all done by ZygoteConnection
[ZygoteServer.java]
Runnable runSelectLoop(String abiList) {// Create two arrays // socketFDs holds some fd, SocketFDs = new ArrayList<FileDescriptor>(); ZygoteConnection ArrayList<FileDescriptor> socketFDs = new ArrayList<FileDescriptor>(); ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>(); / / add a fd from ZygoteSocket 0 bit socketFDs. Add (mZygoteSocket. GetFileDescriptor ()); peers.add(null); While (true) {// The first big chunk of logic that handles UsapPool // and does some processing with socketFDs to create a structs poll structure, There are different Zygotes (regular Zygote, WebView Zygote, or AppZygote) depending on the state of the USAP pool of Zygote. . Poll (pollFDs, -1); pollFDs (pollFDs, -1); } catch (ErrnoException ex) { throw new RuntimeException("poll failed", ex); } boolean usapPoolFDRead = false; while (--pollIndex >= 0) { if ((pollFDs[pollIndex].revents & POLLIN) == 0) { continue; } if (pollIndex == 0) {// ZygoteSocket receives a request, ZygoteConnection ZygoteConnection newPeer = acceptCommandPeer(abiList); peers.add(newPeer); socketFDs.add(newPeer.getFileDescriptor()); } else if (pollIndex < usapPoolEventFDIndex) { // Session socket accepted from the Zygote server socket try { ZygoteConnection connection = peers.get(pollIndex); / / process the socket messages final Runnable command = connection. ProcessOneCommand (this); // If (mIsForkChild) {if (command == null) {throw new IllegalStateException("command == null"); } return command; } else {// On the Server 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(pollIndex); socketFDs.remove(pollIndex); } } } catch (Exception e) { if (! MIsForkChild) {// on the server side, so any exceptions that occur here are preceded by // processing the command or by reading/writing the socket. Make a note of this so that // know exactly why you failed. // 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); ZygoteConnection conn = peers. Remove (pollIndex); ZygoteConnection conn = peers. conn.closeSocket(); socketFDs.remove(pollIndex); } else {log. e(TAG, "Caught post-fork exception in child process.", e); throw e; }} finally {// Reset the child flag to stop using mIsForkChild after Runnable; }} else {// USAP pool event FD or USAP report pipeline. // If this is USAP pool event FD, the payload will be the number of USaps that have been deleted. // If this is a USAP report pipe FD, then the payload will be a USAP PID // this is just specialized. long messagePayload = -1; try { byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES]; int readBytes = Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length); if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) { DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(buffer)); messagePayload = inputStream.readLong(); } else { Log.e(TAG, "Incomplete read from USAP management FD of size " + readBytes); continue; } } catch (Exception ex) { if (pollIndex == usapPoolEventFDIndex) { Log.e(TAG, "Failed to read from USAP pool event FD: " + ex.getMessage()); } else { Log.e(TAG, "Failed to read from USAP reporting pipe: " + ex.getMessage()); } continue; } if (pollIndex > usapPoolEventFDIndex) { Zygote.removeUsapTableEntry((int) messagePayload); } usapPoolFDRead = true; If (usapPoolFDRead) {int[] sessionSocketRawFDs = socketDds.sublist (1, socketFDs.size()) .stream() .mapToInt(fd -> fd.getInt$()) .toArray(); final Runnable command = fillUsapPool(sessionSocketRawFDs); if (command ! = null) { return command; }}}}Copy the code
3.3 acceptCommandPeer
[ZygoteServer.java]
Private ZygoteConnection acceptCommandPeer(String abiList) {try {// This socket is a server socket. Return createNewConnection(mzyGotesocket.Accept (), abiList); } catch (IOException ex) { throw new RuntimeException( "IOException during accept()", ex); }}Copy the code
3.4 createNewConnection
[ZygoteServer.java]
protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
throws IOException {
return new ZygoteConnection(socket, abiList);
}
Copy the code
4 ZygoteConnection
4.1 processOneCommand
ZygoteServer creates the ZygoteConnection object and LocalServerSocket. When ZygoteServer receives the data from the Client, it processes the data through processOneCommand. It reads the parameters, parses the parameters, validates them, forks the child, and processes the parent and child logic separately
[ZygoteConnection.java]
Runnable processOneCommand(ZygoteServer zygoteServer) { String args[]; ZygoteArguments parsedArgs = null; FileDescriptor[] descriptors; Args = Zygote. Readentlist (mSocketReader); descriptors = mSocket.getAncillaryFileDescriptors(); }... // parsedArgs = new ZygoteArguments(args); // A series of verification work after getting the parameters... // Check is complete, ForkAndSpecialize (Parsedargs.muid, parsedargs.mgid, parsedargs.mgids, parseDargs.mgids, parseDargs.mgids) parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote, parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mTargetSdkVersion); Try {the if (pid = = 0) {/ / in the child process, handle the child process related logic zygoteServer. SetForkChild (); zygoteServer.closeServerSocket(); IoUtils.closeQuietly(serverPipeFd); serverPipeFd = null; return handleChildProc(parsedArgs, descriptors, childPipeFd, parsedArgs.mStartChildZygote); Ioutits. closeQuietly(childPipeFd);} else {ioutits. closeQuietly(childPipeFd); childPipeFd = null; handleParentProc(pid, descriptors, serverPipeFd); return null; } } finally { IoUtils.closeQuietly(childPipeFd); IoUtils.closeQuietly(serverPipeFd); }}Copy the code
Now that the child process is created, what does handleChildProc do about the parent process logic handleParentProc and the child process logic handleChildProc
4.2 handleChildProc
[ZygoteConnection.java]
private Runnable handleChildProc(ZygoteArguments parsedArgs, FileDescriptor[] descriptors, FileDescriptor pipeFd, Boolean isZygote) {// The child inherits the socket from the parent, which closes closeSocket(); . if (! IsZygote) {// Not the zygote process, So go this branch return ZygoteInit. ZygoteInit (parsedArgs. MTargetSdkVersion, parsedArgs mRemainingArgs, null / * this * /); } else { return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion, parsedArgs.mRemainingArgs, null /* classLoader */); }}}Copy the code
4.3 zygoteInit
There are three main things that zygoteInit does
- RuntimeInit.commonInit()
- ZygoteInit.nativeZygoteInit()
- RuntimeInit.applicationInit
These three methods mainly do some initialization operations, which will be described later in the process startup.
[ZygoteInit.java]
public static final Runnable zygoteInit(int targetSdkVersion, String[] argv,
ClassLoader classLoader) {
if (RuntimeInit.DEBUG) {
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
RuntimeInit.redirectLogStreams();
RuntimeInit.commonInit();
ZygoteInit.nativeZygoteInit();
return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
Copy the code
4.4 RuntimeInit.applicationInit
The applicationInit method is also simpler, and the key is the findStaticMain in the last line
[RuntimeInit.java]
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
...
return findStaticMain(args.startClass, args.startArgs, classLoader);
}
Copy the code
4.5 findStaticMain
[RuntimeInit.java]
protected static Runnable findStaticMain(String className, String[] argv, ClassLoader classLoader) { Class<? > cl; try { cl = Class.forName(className, true, classLoader); } catch (ClassNotFoundException ex) { ... } Method m; try { m = cl.getMethod("main", new Class[] { String[].class }); } catch ... int modifiers = m.getModifiers(); if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) { ... } return new MethodAndArgsCaller(m, argv); }Copy the code
4.6 MethodAndArgsCaller
static class MethodAndArgsCaller implements Runnable { ... public MethodAndArgsCaller(Method method, String[] args) { mMethod = method; mArgs = args; } public void run() { try { mMethod.invoke(null, new Object[] { mArgs }); }... }}Copy the code
Five summarizes
Obviously, applicationInit uses jNI reflection to get a Java Runnable of the Main method of the ActivityThread, which is essentially the Main method of the ActivityThread, and then finally executes the main method through the outermost run so that our application process starts. In contrast, in the parent process, Zygote handles some cleanup work and waits for the next fork. At this point, the workflow of Zygote is basically finished. Of course, the startup of the system is far from finished, which is explained in the next section