This article is based on Android 9.0, code repository address: Android_9.0.0_r45

Article source link:

SystemServer.java

ActivityManagerService.java

Process.java

ZygoteProcess.java

For those unfamiliar with the Zygote and SystemServer startup processes, read the following two articles:

The Pangu and Nuwa of the Java world — Zygote

The oldest son of the Zygote family, SystemServer

Zygote, as the fertilized egg of the Android world, has not completely gone out of its way after successfully propagating the System_server process, and still bears the responsibilities of the fertilized egg. Zygote starts waiting for the client to call by calling the runSelectLoop() method of the ZygoteServer object it holds. The client requests nothing more than to create an application process. In startActivity(), for example, Zygote is asked to create a process if an application is started that does not already have one. The following will be resolved in terms of both sending the request from the client and processing the request from the server.

The client sends the request

The process of startActivity() is not covered here, but will be covered in a future series of articles. We see directly the startProcess() method for creating the process, which is in ActivityManagerService and is abbreviated to AMS.

Process.startProcess()

> ActivityManagerService.java

private ProcessStartResult startProcess(String hostingType, String entryPoint,
        ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
        String seInfo, String requiredAbi, String instructionSet, String invokeWith,
        long startTime) {
    try {
        checkTime(startTime, "startProcess: asking zygote to start proc");
        final ProcessStartResult startResult;
        if (hostingType.equals("webview_service")) {
            startResult = startWebView(entryPoint,
                    app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                    app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                    app.info.dataDir, null.new String[] {PROC_START_SEQ_IDENT + app.startSeq});
        } else {
            // Create a process
            startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                    app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                    app.info.dataDir, invokeWith,
                    new String[] {PROC_START_SEQ_IDENT + app.startSeq});
        }
        checkTime(startTime, "startProcess: returned from zygote!");
        return startResult;
    } finally{ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); }}Copy the code

Call process.start () to create a new Process.

> Process.java

public static final ProcessStartResult start(/ / android. App. ActivityThread, creating process will be called after the main ()methodsfinal String processClass,
                final String niceName, / / process name
                int uid, int gid, int[] gids,
                int runtimeFlags, int mountExternal,
                int targetSdkVersion,
                String seInfo,
                String abi,
                String instructionSet,
                String appDataDir,
                String invokeWith, // Generally, this parameter is not null when creating an application process
                String[] zygoteArgs) {
        return zygoteProcess.start(processClass, niceName, uid, gid, gids,
                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
    }
Copy the code

Continue calling zygoteprocess.start () :

> ZygoteProess.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); }}Copy the code

Call the startViaZygote() method. Zygote is finally in sight.

startViaZygote()

> ZygoteProcess.java

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,
                                                  booleanStartChildZygote, // Whether to clone all states of zygote process String[] extraArgs)
                                                  throws ZygoteStartFailedEx {
    ArrayList<String> argsForZygote = new ArrayList<String>();

    // --runtime-args, --setuid=, --setgid=,
    // and --setgroups= must go first
    // Process parameters
    argsForZygote.add("--runtime-args");
    argsForZygote.add("--setuid=" + uid);
    argsForZygote.add("--setgid=" + gid);
    argsForZygote.add("--runtime-flags=" + runtimeFlags);
    if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
        argsForZygote.add("--mount-external-default");
    } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
        argsForZygote.add("--mount-external-read");
    } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
        argsForZygote.add("--mount-external-write");
    }
    argsForZygote.add("--target-sdk-version=" + targetSdkVersion);

    // --setgroups is a comma-separated list
    if(gids ! =null && gids.length > 0) {
        StringBuilder sb = new StringBuilder();
        sb.append("--setgroups=");

        int sz = gids.length;
        for (int i = 0; i < sz; i++) {
            if(i ! =0) {
                sb.append(', ');
            }
            sb.append(gids[i]);
        }

        argsForZygote.add(sb.toString());
    }

    if(niceName ! =null) {
        argsForZygote.add("--nice-name=" + niceName);
    }

    if(seInfo ! =null) {
        argsForZygote.add("--seinfo=" + seInfo);
    }

    if(instructionSet ! =null) {
        argsForZygote.add("--instruction-set=" + instructionSet);
    }

    if(appDataDir ! =null) {
        argsForZygote.add("--app-data-dir=" + appDataDir);
    }

    if(invokeWith ! =null) {
        argsForZygote.add("--invoke-with");
        argsForZygote.add(invokeWith);
    }

    if (startChildZygote) {
        argsForZygote.add("--start-child-zygote");
    }

    argsForZygote.add(processClass);

    if(extraArgs ! =null) {
        for(String arg : extraArgs) { argsForZygote.add(arg); }}synchronized(mLock) {
        // Socket communication with Zygote process
        returnzygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote); }}Copy the code

The previous string of code deals with parameters, so just skim through. Core is the final openZygoteSocketIfNeeded () and zygoteSendArgsAndGetResult () these two methods. As you can see from the name of the method, Zygote is being socketed. Remember ZygoteInit. The main () method call registerServerSocketFromEnv () method? It creates the server socket in the Zygote process.

openZygoteSocketIfNeeded()

Take a look at the openZygoteSocketIfNeeded() method.

> ZygoteProcess.java

private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
    Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");
    
    // The connection is down or closed
    if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
        try {
            // Enable socket connection
            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;
    }

    // When the primary zygote does not match, try to connect the second zygote
    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);
}
Copy the code

If the socket connection to the Zygote process is not enabled, blocking and retry may occur. The connection calls the zygoteState.connect () method, and ZygoteState is the inner class of ZygoteProcess.

> ZygoteProcess.java

public static class ZygoteState {
       final LocalSocket socket;
       final DataInputStream inputStream;
       final BufferedWriter writer;
       final List<String> abiList;

       boolean mClosed;

       private ZygoteState(LocalSocket socket, DataInputStream inputStream, BufferedWriter writer, List
       
         abiList)
        {
           this.socket = socket;
           this.inputStream = inputStream;
           this.writer = writer;
           this.abiList = abiList;
       }

       public static ZygoteState connect(LocalSocketAddress address) throws IOException {
           DataInputStream zygoteInputStream = null;
           BufferedWriter zygoteWriter = null;
           final LocalSocket zygoteSocket = new LocalSocket();

           try {
               zygoteSocket.connect(address);

               zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());

               zygoteWriter = new BufferedWriter(new OutputStreamWriter(
                       zygoteSocket.getOutputStream()), 256);
           } catch (IOException ex) {
               try {
                   zygoteSocket.close();
               } catch (IOException ignore) {
               }

               throw ex;
           }

           String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
           Log.i("Zygote"."Process: zygote socket " + address.getNamespace() + "/"
                   + address.getName() + " opened, supported ABIS: " + abiListString);

           return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
                   Arrays.asList(abiListString.split(","))); }... }Copy the code

Connect to Zygote remote server through socket.

Before looking back zygoteSendArgsAndGetResult () method.

zygoteSendArgsAndGetResult()

 > ZygoteProcess.java
 
private static Process.ProcessStartResult zygoteSendArgsAndGetResult( ZygoteState zygoteState, ArrayList
       
         args)
       
       throws ZygoteStartFailedEx {
   try{...final BufferedWriter writer = zygoteState.writer;
       final DataInputStream inputStream = zygoteState.inputStream;

       writer.write(Integer.toString(args.size()));
       writer.newLine();

       // Send parameters to the zygote process
       for (int i = 0; i < sz; i++) {
           String arg = args.get(i);
           writer.write(arg);
           writer.newLine();
       }

       writer.flush();

       // Should there be a timeout?
       Process.ProcessStartResult result = new Process.ProcessStartResult();

       // Always read the entire result from the input stream to avoid leaving
       // bytes in the stream for future process starts to accidentally stumble
       // upon.
       // Read the child PID returned by zygote
       result.pid = inputStream.readInt();
       result.usingWrapper = inputStream.readBoolean();

       if (result.pid < 0) { // Pid is less than 0, fork failed
           throw new ZygoteStartFailedEx("fork() failed");
       }
       return result;
   } catch (IOException ex) {
       zygoteState.close();
       throw newZygoteStartFailedEx(ex); }}Copy the code

Send the request parameters over the socket and wait for the Zygote process to return the child PID. At this point, the client is done for the time being, and we can trace back to the server to see how the server handles the client request.

Zygote handles client requests

Zygote processing client requests the code in ZygoteServer. RunSelectLoop () method.

> ZygoteServer.java

Runnable runSelectLoop(String abiList) {...while (true) {...try {
           // Execute down when there is an event, block when there is no event
           Os.poll(pollFds, -1);
       } catch (ErrnoException ex) {
           throw new RuntimeException("poll failed", ex);
       }
       for (int i = pollFds.length - 1; i >= 0; --i) {
           if ((pollFds[i].revents & POLLIN) == 0) {
               continue;
           }

           if (i == 0) { // There is a new client connection
               ZygoteConnection newPeer = acceptCommandPeer(abiList);
               peers.add(newPeer);
               fds.add(newPeer.getFileDesciptor());
           } else { // Process client requests
               try {
                   ZygoteConnection connection = peers.get(i);
                   // fork the child and return the Runnable object containing the main() function of the child
                   final Runnable command = connection.processOneCommand(this);

                   if (mIsForkChild) {
                       // in the child process
                       if (command == null) {
                           throw new IllegalStateException("command == null");
                       }

                       return command;
                   } else {
                       // in the parent process
                       if(command ! =null) {
                           throw new IllegalStateException("command ! = null");
                       }

                       if(connection.isClosedByPeer()) { connection.closeSocket(); peers.remove(i); fds.remove(i); }}}catch (Exception e) {
                   ...
               } finally {
                   mIsForkChild = false;
               }
           }
       }
   }
}
Copy the code

The acceptCommandPeer() method is used to respond to a socket connection request from a new client. The processOneCommand() method is used to handle general requests from the client.

processOneCommand()

> ZygoteConnection.java

Runnable processOneCommand(ZygoteServer zygoteServer) {
    String args[];
    Arguments parsedArgs = null;
    FileDescriptor[] descriptors;

    try {
        1. Read the parameter list sent by the socket client
        args = readArgumentList();
        descriptors = mSocket.getAncillaryFileDescriptors();
    } catch (IOException ex) {
        throw new IllegalStateException("IOException on command socket", ex); }...// 2. Fork child process
    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) {
            // Is in the forward process
            zygoteServer.setForkChild();
            // Close the server socket
            zygoteServer.closeServerSocket();
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;
            // 3. Process child process transactions
            return handleChildProc(parsedArgs, descriptors, childPipeFd,
                    parsedArgs.startChildZygote);
        } else {
            // Is in the Zygote process
            IoUtils.closeQuietly(childPipeFd);
            childPipeFd = null;
            // 4. Process the parent process
            handleParentProc(pid, descriptors, serverPipeFd);
            return null; }}finally{ IoUtils.closeQuietly(childPipeFd); IoUtils.closeQuietly(serverPipeFd); }}Copy the code

The processOneCommand() method can be roughly divided into five steps, which are analyzed step by step.

readArgumentList()

> ZygoteConnection.java

private String[] readArgumentList()
        throws IOException {

    int argc;

    try {
        // Read arguments line by line
        String s = mSocketReader.readLine();

        if (s == null) {
            // EOF reached.
            return null;
        }
        argc = Integer.parseInt(s);
    } catch (NumberFormatException ex) {
        throw new IOException("invalid wire format");
    }

    // See bug 1092107: large argc can be used for a DOS attack
    if (argc > MAX_ZYGOTE_ARGC) {
        throw new IOException("max arg count exceeded");
    }

    String[] result = new String[argc];
    for (int i = 0; i < argc; i++) {
        result[i] = mSocketReader.readLine();
        if (result[i] == null) {
            // We got an unexpected EOF.
            throw new IOException("truncated request"); }}return result;
}
Copy the code

Read the request parameters sent by the client.

forkAndSpecialize()

> Zygote.java

public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
      int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
      int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) {
    VM_HOOKS.preFork();
    // Resets nice priority for zygote process.
    resetNicePriority();
    int pid = nativeForkAndSpecialize(
              uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
              fdsToIgnore, startChildZygote, instructionSet, appDataDir);
    // Enable tracing as soon as possible for the child process.
    if (pid == 0) {
        Trace.setTracingEnabled(true, runtimeFlags);

        // Note that this event ends at the end of handleChildProc,
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
    }
    VM_HOOKS.postForkCommon();
    return pid;
}
Copy the code

NativeForkAndSpecialize () is a native method that forks a new process at the bottom and returns its PID. Don’t forget one fork here, two returns. Pid > 0 indicates the parent process. Pid = 0 indicates that the subprocess is entered. HandleChildProc is called in the child process and handleParentProc() is called in the parent process.

handleChildProc()

> ZygoteConnection.java

private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
        FileDescriptor pipeFd, boolean isZygote) {
    closeSocket(); // Close the socket connection.if(parsedArgs.niceName ! =null) {
        // Set the process name
        Process.setArgV0(parsedArgs.niceName);
    }

    if(parsedArgs.invokeWith ! =null) {
        WrapperInit.execApplication(parsedArgs.invokeWith,
                parsedArgs.niceName, parsedArgs.targetSdkVersion,
                VMRuntime.getCurrentInstructionSet(),
                pipeFd, parsedArgs.remainingArgs);

        // Should not get here.
        throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
    } else {
        if(! isZygote) {// The isZygote parameter is false when creating an application process
            return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
                    null /* classLoader */);
        } else {
            return ZygoteInit.childZygoteInit(parsedArgs.targetSdkVersion,
                    parsedArgs.remainingArgs, null /* classLoader */); }}}Copy the code

You should be familiar with zygoteinit.zygoteinit ().

ZygoteInit.zygoteInit() -> RuntimeInit.applicationInit() -> findStaticMain()

This is the same as the process for creating the SystemServer process. The main method to look for is activitythrad.main (). ActivityThread is not a thread, but you can think of it as the main thread of your application.

handleParentProc()

> ZygoteConnection.java

private void handleParentProc(int pid, FileDescriptor[] descriptors, FileDescriptor pipeFd) {
        if (pid > 0) {
            setChildPgid(pid);
        }

        if(descriptors ! =null) {
            for(FileDescriptor fd: descriptors) { IoUtils.closeQuietly(fd); }}boolean usingWrapper = false;
        if(pipeFd ! =null && pid > 0) {
            int innerPid = -1;
            try {
                // Do a busy loop here. We can't guarantee that a failure (and thus an exception
                // bail) happens in a timely manner.
                final int BYTES_REQUIRED = 4;  // Bytes in an int.

                StructPollfd fds[] = new StructPollfd[] {
                        new StructPollfd()
                };

                byte data[] = new byte[BYTES_REQUIRED];

                int remainingSleepTime = WRAPPED_PID_TIMEOUT_MILLIS;
                int dataIndex = 0;
                long startTime = System.nanoTime();

                while (dataIndex < data.length && remainingSleepTime > 0) {
                    fds[0].fd = pipeFd;
                    fds[0].events = (short) POLLIN;
                    fds[0].revents = 0;
                    fds[0].userData = null;

                    int res = android.system.Os.poll(fds, remainingSleepTime);
                    long endTime = System.nanoTime();
                    int elapsedTimeMs = (int)((endTime - startTime) / 1000000l);
                    remainingSleepTime = WRAPPED_PID_TIMEOUT_MILLIS - elapsedTimeMs;

                    if (res > 0) {
                        if ((fds[0].revents & POLLIN) ! =0) {
                            // Only read one byte, so as not to block.
                            int readBytes = android.system.Os.read(pipeFd, data, dataIndex, 1);
                            if (readBytes < 0) {
                                throw new RuntimeException("Some error");
                            }
                            dataIndex += readBytes;
                        } else {
                            // Error case. revents should contain one of the error bits.
                            break; }}else if (res == 0) {
                        Log.w(TAG, "Timed out waiting for child."); }}if (dataIndex == data.length) {
                    DataInputStream is = new DataInputStream(new ByteArrayInputStream(data));
                    innerPid = is.readInt();
                }

                if (innerPid == -1) {
                    Log.w(TAG, "Error reading pid from wrapped process, child may have died"); }}catch (Exception ex) {
                Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
            }

            // Ensure that the pid reported by the wrapped process is either the
            // child process that we forked, or a descendant of it.
            if (innerPid > 0) {
                int parentPid = innerPid;
                while (parentPid > 0&& parentPid ! = pid) { parentPid = Process.getParentPid(parentPid); }if (parentPid > 0) {
                    Log.i(TAG, "Wrapped process has pid " + innerPid);
                    pid = innerPid;
                    usingWrapper = true;
                } else {
                    Log.w(TAG, "Wrapped process reported a pid that is not a child of "
                            + "the process that we forked: childPid=" + pid
                            + " innerPid="+ innerPid); }}}try {
            mSocketOutStream.writeInt(pid);
            mSocketOutStream.writeBoolean(usingWrapper);
        } catch (IOException ex) {
            throw new IllegalStateException("Error writing to command socket", ex); }}Copy the code

The main task is to clean up resources. At this point, the child process is created.

conclusion

  1. callProcess.start()Creating an Application Process
  2. ZygoteProcessResponsible andZygoteThe process establishes the socket connection and sends the parameters needed to create the process to the Socket server of Zygote
  3. ZygoteCalled when the server receives the parameterZygoteConnection.processOneCommand()Process parameters and fork the process
  4. At last,findStaticMain()findActivityThreadThe main() method of the class is executed and the child process starts

trailer

So far we have resolved the Zygote process, SystemServer process, and application process creation. The next article will look at ActivityManagerService, the most relevant system service for applications, and see how it is created and started in SystemServer. Stay tuned!

This article first published wechat official account: BingxinshuaiTM, focusing on Java, Android original knowledge sharing, LeetCode problem solving.

More latest original articles, scan code to pay attention to me!