I’m participating in nuggets Creators Camp # 4, click here to learn more and learn together!

Application Process Startup Process Analysis (Server)

In the previous analysis of the application Process startup Process (client), it was analyzed that when the process. start function was called, the corresponding connection was established through the socket corresponding to zygote, and then the parameter data was sent to the server through the socket communication. Then the server received the corresponding parameters, how to Process it?

Zygoteinit. main (zygoteinit. main) ¶ Zygoteinit. main (zygoteinit. main) ¶ The system_server process is forked, but a ZygoteServer object is initialized

public static void main(String argv[]) {
    ZygoteServer zygoteServer = null;

    / / /...
    Runnable caller;
    try {
        / /...
        Initialize the ZygoteServer object
        zygoteServer = new ZygoteServer(isPrimaryZygote);

        / /...

        // The select loop returns early in the child process after a fork and
        // loops forever in the zygote.
        caller = zygoteServer.runSelectLoop(abiList);
    }
    / /...
    // We're in the child process and have exited the select loop. Proceed to execute the
    // command.
    if(caller ! =null) { caller.run(); }}Copy the code

After the ZygoteServer object is initialized, its runSelectLoop function is called and a Runnable object is returned, which is then run

In the runSelectLoop function, a while infinite loop is started, waiting for the Socket to connect

Runnable runSelectLoop(String abiList) {
    / /...

    while (true) {
        / /...

        int pollReturnValue;
        try {
            // Iterate over the polling all as pollFDs
            pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
        }
        / /...
        {
            boolean usapPoolFDRead = false;

            while (--pollIndex >= 0) {
                / /...
                // When the ZygoteServer is 0, it indicates that there is a client to connect after ZygoteServer starts
                if (pollIndex == 0) {
                    Call the acceptCommandPeer function to initialize a ZygoteConnection object after receiving a client connection request
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
                    peers.add(newPeer);
                    socketFDs.add(newPeer.getFileDescriptor());
                // At least one ZygoteConnection connection has been established
                } else if (pollIndex < usapPoolEventFDIndex) {
                    / /...
                    try {
                        // Get the corresponding ZygoteConnection object and call its processOneCommand function
                        ZygoteConnection connection = peers.get(pollIndex);
                        final Runnable command = connection.processOneCommand(this);
                        log("runSelectLoop : mIsForkChild = " + mIsForkChild);
                        // TODO (chriswailes): Is this extra check necessary?
                        if (mIsForkChild) {
                            // We're in the child. We should always have a command to run at
                            // this stage if processOneCommand hasn't called "exec".
                            if (command == null) {
                                throw new IllegalStateException("command == null");
                            }

                            returncommand; }}/ /...}}/ /...
        }
        / /...}}Copy the code

When ZygoteServer is initialized and runSelectLoop is called, an infinite while loop is initiated. When a client connection request is received, the acceptCommandPeer function is called, Initialization establishes link ZygoteConnection

When a message request is received, since the peers contain a ZygoteConnection link, the link is retrieved, its processOneCommand function is called, a thread is returned, and the thread’s run function is finally called in zygoteinit. main

i.e.

Create a ZygoteConnection link

private ZygoteConnection acceptCommandPeer(String abiList) {
    / /...
    return createNewConnection(mZygoteSocket.accept(), abiList);
}

protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
        throws IOException {
    return new ZygoteConnection(socket, abiList);
}
Copy the code

That is, initialize a ZygoteConnection object in a ZygoteServer

Run the ZygoteConnection processOneCommand function

Runnable processOneCommand(ZygoteServer zygoteServer) {
    String[] args;

    / /...
    // Read the parameter data from zygote socket
    args = Zygote.readArgumentList(mSocketReader);
    / /...
    // Convert to an array of recognizable parameters
    ZygoteArguments parsedArgs = new ZygoteArguments(args);

    / /... Parameter configuration

    // Call Zygote's Forkandwpecte function
    pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
            parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
            parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
            parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
            parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList,
            parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);
    
    / /...
    return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
    / /...
}
Copy the code

Zygote. Forkandwte ends up calling the Native layer’s fork function. Fork a Launcher application in the kernel and, after a series of operations, Will call ZygoteConnection handleChildProc function

private Runnable handleChildProc(ZygoteArguments parsedArgs,
        FileDescriptor pipeFd, boolean isZygote) {
    / /...
    // Close the current SOCKET link
    closeSocket();
    // Set the process name
    Zygote.setAppProcessName(parsedArgs, TAG);

    / /...
    // Through the ZygoteInit function of the ZygoteInit object
    return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
            parsedArgs.mDisabledCompatChanges,
            parsedArgs.mRemainingArgs, null /* classLoader */);
    / /...
}

public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
        String[] argv, ClassLoader classLoader) {
    / /...
    return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
            classLoader);
}

protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
        String[] argv, ClassLoader classLoader) {
    / /...
    return findStaticMain(args.startClass, args.startArgs, classLoader);
}

protected static Runnable findStaticMain(String className, String[] argv, ClassLoader classLoader) { Class<? > cl;try {
        // Find the corresponding Class
        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 function in the corresponding Class
        m = cl.getMethod("main".new Class[] { String[].class });
    } catch (NoSuchMethodException ex) {
        throw new RuntimeException(
                "Missing static main on " + className, ex);
    } catch (SecurityException ex) {
        throw new RuntimeException(
                "Problem getting static main on " + className, ex);
    }

    int modifiers = m.getModifiers();
    if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
        throw new RuntimeException(
                "Main method is not public and static on " + className);
    }

    / /...
    // Finally returns the MethodAndArgsCaller object
    return new MethodAndArgsCaller(m, argv);
}
Copy the code

As you can see from the flow above, a MethodAndArgsCaller object is finally initialized and returned from its class diagram structure

ClassDiagram Runnable < | - MethodAndArgsCaller: < < interface > > Runnable RuntimeInit * -- MethodAndArgsCaller: inner classes

Returns an object that implements the Runnable interface

To sort out the process,

  1. In ZygoteInit’s main function, we initialize a ZygoteServer object,
  2. It then calls its runSelectLoop function, which implements a while loop in the main thread,
  3. In this infinite loop, the zygote socket client is waiting for a connection. Once the connection request is received, the acceptCommandPeer function initializes a ZygoteConnection link and calls the processOneCommand function of the link.
  4. The JNI function forks an application process in kernel space, and eventually calls ZygoteConnection’s handleChildProc through a series of process scheduling operations, and finally returns a MethodAndArgsCaller object. The MethodAndArgsCaller object is an object that implements the Runnable interface

Run the MethodAndArgsCaller object obtained above

Back in ZygoteInit’s main function, you can see that runSelectLoop does not return null for MethodAndArgsCaller, so run the Runnable thread

ZygoteInit.main
if (caller ! =null) {
    caller.run();
}

static class MethodAndArgsCaller implements Runnable {
    /** method to call */
    private final Method mMethod;

    /** argument array */
    private final String[] mArgs;

    public MethodAndArgsCaller(Method method, String[] args) {
        mMethod = method;
        mArgs = args;
    }

    public void run(a) {
        / /...
        mMethod.invoke(null.new Object[] { mArgs });
        / /...}}Copy the code

According to the previous analysis, this MethodAndArgsCaller mMethod parameters corresponding to the android. OS. ActivityThread main function of a class, and then through reflection calls to this function

public static void main(String[] args) {
    / /...

    Environment.initForCurrentUser();

    // Make sure TrustedCertificateStore looks in the right place for CA certificates
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);

    // Call per-process mainline module initialization.
    initializeMainlineModules();

    Process.setArgV0("<pre-initialized>");

    Looper.prepareMainLooper();

    // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
    // It will be in the format "seq=114"
    long startSeq = 0;
    if(args ! =null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if(args[i] ! =null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                startSeq = Long.parseLong(
                        args[i].substring(PROC_START_SEQ_IDENT.length()));
            }
        }
    }
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    / /...
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}
Copy the code

Finally, the specific Activity of the corresponding application is launched in the ActivityThread object, which will be updated later

Flow sequence diagram