I am participating in the Mid-Autumn Festival Creative Submission contest, please see: Mid-Autumn Festival Creative Submission Contest for details

Android Zygote Launch (AOSP 11)

AndroidRuntime. Start:

Androidruntime.start () : ZygoteInit (); androidRuntime.start () : ZygoteInit ();

Zygote, which means “fertilized egg” in Chinese and is often called an incubator, is an apt name for Android App processes and SystemServer processes, which are all created by Zygote processes.

When Zygote starts, it creates an AndroidRuntime. When a child process is created through the fork call, the child process also has an AndroidRuntime.

The main function of art. start is to start a JVM by calling the static void main method with a ClassName specified:

// The first argument is className (not simpleName) and the second argument is the argument to be passed to main
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    // ...
    static const String8 startSystemServer("start-system-server");
    // Whether this is the primary zygote, meaning the zygote which will fork system server.
    bool primary_zygote = false;

    Zygote mode is enabled if the startup parameter contains start-system-server
    for (size_t i = 0; i < options.size(); ++i) {
        if (options[i] == startSystemServer) {
            primary_zygote = true;
           /* track our progress through the boot sequence */
           const int LOG_BOOT_PROGRESS_START = 3000; LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); }}// ...
    // Omit some environment parameter Settings
    // ...

    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    // Create a JVM instance by calling JNI_CreateJavaVM
    if(startVm(&mJavaVM, &env, zygote, primary_zygote) ! =0) {
        return;
    }
    onVmCreated(env);

   	// Register various Android-related JNI functions into the JVM, such as Log, Binder, etc
    Specific can see / / base/core/jni/AndroidRuntime CPP in the code
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    // Encapsulate the various parameters to be passed to main

    // Ready to start JVM
    // Replace '.' with '/' for className
    char* slashClassName = toSlashClassName(className ! = NULL ? className :"");
    // Find the Class to start
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        // Find the main method of the Class to start
        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 {
            // Execute the main method of the Class to be started
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }
    // If the program is running properly, it will not get there
  	// ...
}
Copy the code

So the core of what Art.start does is:

  1. Start a JVM.
  2. Register the JNI provided by Android with the JVM started.
  3. Find the ZygoteInit class and execute its main function.

At this point, Zygote’s startup has moved from Native Framework to Java Framework.

ZygoteInit:

In the frameworks/base/core/Java/com/android/internal/OS/ZygoteInit. In Java:

public static void main(String[] argv) {
        // ...
        try {
            // ...
            boolean startSystemServer = false;
            String zygoteSocketName = "zygote";
            String abiList = null;
            boolean enableLazyPreload = false;
            for (int i = 1; i < argv.length; i++) {
                // SystemServer needs to be started
                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)) {
                    // Get the socket name of zygote process
                    zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
                } else {
                    throw new RuntimeException("Unknown command line argument: "+ argv[i]); }}if(! enableLazyPreload) {// Preload classes and resources
                preload(bootTimingsTraceLog);
            }
            // Create ZygoteServer object.
            zygoteServer = new ZygoteServer(isPrimaryZygote);
            if (startSystemServer) {
                // fork SystemServer
                Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
                if(r ! =null) {
                    r.run();
                    return; }}// start Zygote select loop
            caller = zygoteServer.runSelectLoop(abiList);
        } catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex);
            throw ex;
        } finally {
            if(zygoteServer ! =null) { zygoteServer.closeServerSocket(); }}if(caller ! =null) { caller.run(); }}Copy the code

Create the ZygoteServer object as follows:

   ZygoteServer(boolean isPrimaryZygote) {
        mUsapPoolEventFD = Zygote.getUsapPoolEventFD();
        if (isPrimaryZygote) {
            // If it is the main Zygote process, the socket will be created with the name ANDROID_SOCKET_zygote
            // The maximum number of simultaneous connections monitored by the socket is 50
            // frameworks/base/core/java/android/net/LocalServerSocket.java
            mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
            mUsapPoolSocket =
                    Zygote.createManagedSocketFromInitSocket(
                            Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);
        } else {
            // The same is true for assisted Zygote processes
            mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME);
            mUsapPoolSocket =
                    Zygote.createManagedSocketFromInitSocket(
                            Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);
        }

        mUsapPoolSupported = true;
        // Configure the attributes associated with USAP, a new mechanism introduced in Android 10
        // Fork out a batch of processes ahead of time, when new processes need to be created
        // Allocate pre-created processes directly, eliminating temporary forks to improve performance.
        fetchUsapPoolPolicyProps();
    }
    
// Zygote.createManagedSocketFromInitSocket
    static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
        int fileDesc;
        final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
        try {
            // Get the value of the socket environment variable
            String env = System.getenv(fullSocketName);
            // Convert environment variables to file descriptor parameters
            fileDesc = Integer.parseInt(env);
        } catch (RuntimeException ex) {
            throw new RuntimeException("Socket unset or invalid: " + fullSocketName, ex);
        }
        try {
            // Create a file descriptor for the Socket
            FileDescriptor fd = new FileDescriptor();
            fd.setInt$(fileDesc);
            // Create LocalServerSocket object
            return new LocalServerSocket(fd);
        } catch (IOException ex) {
            throw new RuntimeException(
                "Error building socket from file descriptor: "+ fileDesc, ex); }}// Impl. Listen enables listening on the fd
 // THE communication between AMS and Zygote is socket-based, and subsequent AMS can pass through this Socket
 // Communicate with Zygote.
 public LocalServerSocket(FileDescriptor fd) throws IOException
    {
        impl = new LocalSocketImpl(fd);
        impl.listen(LISTEN_BACKLOG);
        localAddress = impl.getSockAddress();
    }
Copy the code

[forkSystemServer] [forkSystemServer] [forkSystemServer]

   private static Runnable forkSystemServer(String abiList, String socketName, ZygoteServer zygoteServer) {
        // omit: some configuration SystemServer startup parameters,
        / / contains such as start class called com. Android. Server SystemServer
        int pid;
        try {
            // fork Create SystemServer process
            pid = Zygote.forkSystemServer(
                    parsedArgs.mUid, parsedArgs.mGid,
                    parsedArgs.mGids,
                    parsedArgs.mRuntimeFlags,
                    null,
                    parsedArgs.mPermittedCapabilities,
                    parsedArgs.mEffectiveCapabilities);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }
        // pid==0 Indicates a subprocess. The subprocess of zygote is the SystemServer process
        if (pid == 0) {
            // If the auxiliary Zygote process is required, the same socket connection will be created for the auxiliary Zygote process
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }
            // Since the SystemServer process forks from the Zygote process, it naturally copies the socket connection including Zygote
            // However, these connections are not useful to SystemServer, so they need to be closed.
            zygoteServer.closeServerSocket();
            // Process the SystemServer process
            return handleSystemServerProcess(parsedArgs);
        }
        return null;
    }
Copy the code

In the above code, the core is Zygote. ForkSystemServer, through the native forkSystemServer call, complete the creation of the SystemServer process. The implementation of nativeForkSystemServer is in base/core/jni/ com_Android_internal_OS_zygote. CPP. The core implementation is to create a process in ForkCommon through a fork call. It also does some system signal Handler setup work. Fork out the SystemServer after the process, in handleSystemServerProcess series of call back, will eventually find SystemServer Class and the main method, start SystemServer.

Going back to ZygoteInit:

// start Zygote select loop
caller = zygoteServer.runSelectLoop(abiList);
Copy the code

RunSelectLoop is implemented as follows:

    Runnable runSelectLoop(String abiList) {
        ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
        // Link to Zygote process
        ArrayList<ZygoteConnection> peers = new ArrayList<>();
        // The Socket to be created by the Zygote process (i.e. PRIMARY_SOCKET_NAME mentioned below)
        // Add to socketFDs
        socketFDs.add(mZygoteSocket.getFileDescriptor());
        peers.add(null);
        mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
        // Infinite loop
        while (true) {
            // ...
            int[] usapPipeFDs = null;
            StructPollfd[] pollFDs;
            // Save socetFD stored information to StructPollfd
            int pollIndex = 0;
            for (FileDescriptor socketFD : socketFDs) {
                pollFDs[pollIndex] = new StructPollfd();
                pollFDs[pollIndex].fd = socketFD;
                pollFDs[pollIndex].events = (short) POLLIN;
                ++pollIndex;
            }
            // ...
            // Wait for data to be written to the Socket corresponding to pollFDs
          	try {
                pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
            } catch (ErrnoException ex) {
                throw new RuntimeException("poll failed", ex);
            }
            // ...
            if (pollReturnValue == 0) {
                / / to omit
            } else {
                boolean usapPoolFDRead = false;
                while (--pollIndex >= 0) {
                    if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
                        continue;
                    }

                    if (pollIndex == 0) {
                        // If pollIndex is 0, a process connects to the Zygote Socket (e.g. AMS),
                        // a ZygoteConnection will be created and added to the connection pool of the Zygote Socket
                        // Connetion is associated with the socket
                        // The name of the Zygete created during startup is PRIMARY_SOCKET_NAME
                        // mZygoteSocket
                        ZygoteConnection newPeer = acceptCommandPeer(abiList);
                        peers.add(newPeer);
                        // Add the FD of ZygeteConnection to socketFDs
                        // The request sent by AMS was received.
                        socketFDs.add(newPeer.getFileDescriptor());
                    } else if (pollIndex < usapPoolEventFDIndex) {
                        // pollIndex is not 0, indicating that, for example, AMS has sent a request to Zygote
                        // In the meantime, processes can be created through the USAP mechanism
                        try {
                            ZygoteConnection connection = peers.get(pollIndex);
                            booleanmultipleForksOK = ! isUsapPoolEnabled() && ZygoteHooks.isIndefiniteThreadSuspensionSafe();// An internal call to Zygote's forkSimpleApp creates an application process
                            final Runnable command =
                                    connection.processCommand(this, multipleForksOK);
                          }
    // ...
    }
Copy the code

Back to see startViaZygote:

Before the Activity start process analysis, we know that the new process of creating the App is ultimately through ZygoteProcess. StartViaZygote calls. Now let’s see what happens behind this function:

StartViaZygote is as follows:

// ...
// a lot of logic to assemble parameters based on input parameters
/ / the last
synchronized(mLock) {
	// The USAP pool can not be used if the application will not use the systems graphics
	// driver. If that driver is requested use the Zygote application start path.
	return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
		zygotePolicyFlags,
		argsForZygote);
}
Copy the code

ZygoteSendArgsAndGetResult – > attemptZygoteSendArgsAndGetResult, will launch parameters processing in zygoteSendArgsAndGetResult into a string, Pass to attemptZygoteSendArgsAndGetResult

    private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult( ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
        try {
            final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
            final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;
						// Various information about the process to be started is written to zygoteWriter's buffer.
            zygoteWriter.write(msgStr);
            / / refresh buffer
            zygoteWriter.flush();
						
            Process.ProcessStartResult result = new Process.ProcessStartResult();
            // The new process has been created.
            result.pid = zygoteInputStream.readInt();
            result.usingWrapper = zygoteInputStream.readBoolean();

            if (result.pid < 0) {
                throw new ZygoteStartFailedEx("fork() failed");
            }

            return result;
        } catch (IOException ex) {
            zygoteState.close();
            Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "
                    + ex.toString());
            throw newZygoteStartFailedEx(ex); }}Copy the code

BufferedWriter and BufferedReader are buffered character input and output streams. On write, the Buffer is written to the Buffer, and on Flush, the Buffer is written to the destination file. So we need to know where mZygoteOutputWriter is actually writing data,

MZygoteOutputWriter is assigned when the ZygoteState object is created:

private ZygoteState(LocalSocketAddress zygoteSocketAddress, LocalSocketAddress usapSocketAddress, LocalSocket zygoteSessionSocket, DataInputStream zygoteInputStream, BufferedWriter zygoteOutputWriter, List
       
         abiList)
        {
		this.mZygoteSocketAddress = zygoteSocketAddress;
		this.mUsapSocketAddress = usapSocketAddress;
		this.mZygoteSessionSocket = zygoteSessionSocket;
		this.mZygoteInputStream = zygoteInputStream;
		this.mZygoteOutputWriter = zygoteOutputWriter;
		this.mAbiList = abiList;
}
Copy the code

ZygoteState is created in the Connect method of ZygoteState:

        static ZygoteState connect(@NonNull LocalSocketAddress zygoteSocketAddress,
                @Nullable LocalSocketAddress usapSocketAddress)
                throws IOException {

            DataInputStream zygoteInputStream;
            BufferedWriter zygoteOutputWriter;
            // Create a LocalSocket object
            final LocalSocket zygoteSessionSocket = new LocalSocket();

            if (zygoteSocketAddress == null) {
                throw new IllegalArgumentException("zygoteSocketAddress can't be null");
            }

            try {
                // Connect the LocalSocket object to zygoteSocketAddress
                // Establish a connection with the server socket
                ZygoteSocketAddress Socket fd
                zygoteSessionSocket.connect(zygoteSocketAddress);
                zygoteInputStream = new DataInputStream(zygoteSessionSocket.getInputStream());
                // zygoteOutputWriter writes to the output stream of the zygoteSessionSocket.
                zygoteOutputWriter =
                        new BufferedWriter(
                                new OutputStreamWriter(zygoteSessionSocket.getOutputStream()),
                                Zygote.SOCKET_BUFFER_SIZE);
            } catch (IOException ex) {
                try {
                    zygoteSessionSocket.close();
                } catch (IOException ignore) { }

                throw ex;
            }

            return new ZygoteState(zygoteSocketAddress, usapSocketAddress,
                                   zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter,
                                   getAbiList(zygoteOutputWriter, zygoteInputStream));
        }
Copy the code

So we know that the data written by mZygoteOutputWriter will be read by the Socket corresponding to zygoteSocketAddress, and zygoteSocketAddress will be read by the Socket corresponding to zygoteSocketAddress. Is the mZygoteSocketAddress of ZygoteProcess. In the constructor of ZygoteProcess:

mZygoteSocketAddress =
new LocalSocketAddress(Zygote.PRIMARY_SOCKET_NAME,
	LocalSocketAddress.Namespace.RESERVED);
Copy the code

PRIMARY_SOCKET_NAME is “zygote”

When is the Socket read from PRIMARY_SOCKET_NAME? Back in ZygoteInit’s runSelectLoop, while other processes like AMS are connecting to Zygote, PRIMARY_SOCKET_NAME is actually associated with the Socket object held by the ZygoteServer object created during Zygote startup. PRIMARY_SOCKET_NAME is then included in the FDS of the stack of sockets observed by the poll mechanism in Zygote’s runSelectloop.

When there is a request process created data written to the PRIMARY_SOCKET_NAME this Socket, runSelectloop in an infinite loop will be awakened, and then call connection. ProcessCommand, among them, if it is App process creation, ForkSimpleApps is called to Zygote, and finally, through Zygote ::forkApp -> Zygote ::ForkCommon, ForkCommon, and ForkCommon, a child is created. Zygote then tweaks some of the child’s configuration, and a new App process is created.