preface

I plan to write a series of articles, starting from Zygote, talking about Activity, and then View display and event distribution, aiming to string together some of the most core knowledge points in Android development and see how Android organizes them together. I hope I can write well.

This article is the first one, which is entitled “Zygote startup process and operation mechanism”, and will open the line of “Virtual machine -Zygote- application process -ActivityThread”.

Zygote, which means Zygote in Chinese, is an incubator — most Android applications and system processes are powered by Zygote.

PS: source code based on Android API 27.

How did Zygote get started?

init

The first Android process is init, which parses init.rc to start other key system service processes — the most important of which are ServiceManager, Zygote, and SystemServer. The following uses init.zygote64.rc as an example:

Service name, path, parameter
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    writepid /dev/cpuset/foreground/tasks
Copy the code

App_process can identify whether zygote needs to be started by specifying the –zygote parameter.

Start the VM

Let’s start by analyzing the file app_main.cpp in path app_process:

CPP int main(int argc, char* const argv[]) {AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv)); . // Parse runtime arguments. Stop at first unrecognized option. bool zygote =false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

    ++i;  // Skip unused "parent dir" argument.
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {// // init.rc specifies the argument --zygote, so here istrue
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {// init.rc specifies the argument --start-system-servertrue
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {... }else{... } } Vector<String8> args;if(! className.isEmpty()) { ... }else {
        if (startSystemServer) {
            args.add(String8("start-system-server")); // Add SystemServer parameters}... }...if(zygote) {// Start the vm and zygote with runtime.start("com.android.internal.os.ZygoteInit", args, zygote); 
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); }}Copy the code

Zygote runs on an Android vm because AppRuntime inherits from AndroidRuntime:

class AppRuntime : public AndroidRuntime
{
public:
    AppRuntime(char* argBlockStart, const size_t argBlockLength)
        : AndroidRuntime(argBlockStart, argBlockLength)
        , mClass(NULL)
    {
    }
    
    ...
    
}
Copy the code

AppRuntime is more about handling callback after some event is done, and the main implementation is still in AndroidRuntime. So let’s get straight to the implementation of AndroidRumtime::start:

/*
* Start the Android runtime.  This involves starting the virtual machine
* and calling the "static void main(String[] args)" method in the class
* named by "className".
*
* Passes the main functionArguments, the class name and the specified * options string. * * This method is used to start the Android VM. Void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) { ... /* Start the invocation */ Jni_Invocation; jni_invocation.Init(NULL); // Initialize the vm environment JNIEnv* env;if(startVm(&mJavaVM, &env, zygote) ! = 0) {// startreturn; } onVmCreated(env); /* * Register Android native function */if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    /*
     * We want to call main() with a String array with arguments init. * At present we have two arguments, The class name and an option string. * Create an array to hold them. * * Call the main method of the class corresponding to className. And pass in the corresponding argument */ jClass stringClass; jobjectArray strArray; jstring classNameStr; stringClass = env->FindClass("java/lang/String"); strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL); classNameStr = env->NewStringUTF(className); env->SetObjectArrayElement(strArray, 0, classNameStr); // Set parametersfor (size_t i = 0; i < options.size(); ++i) {
        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
        env->SetObjectArrayElement(strArray, i + 1, optionsStr);
    }

    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return* * The current thread becomes the main thread of the VIRTUAL machine. The main method blocks until the VM exits. */ char* slashClassName = toSlashClassName(className! = NULL ? className :""); jclass startClass = env->FindClass(slashClassName); // Find the class corresponding to classNameif (startClass == NULL) {
        ...
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main"."([Ljava/lang/String;)V"); // find the main method corresponding to startClassif(startMeth == NULL) {// Main method not found... }else{ env->CallStaticVoidMethod(startClass, startMeth, strArray); // Call the main method}} // end virtual machine ALOGD("Shutting down VM\n");
    if(mJavaVM->DetachCurrentThread() ! = JNI_OK) ALOGW("Warning: unable to detach main thread\n");
    if(mJavaVM->DestroyJavaVM() ! = 0) ALOGW("Warning: VM did not shut down cleanly\n");
}
Copy the code

The above code is still very easy to understand, the main function is:

  1. Initialize the VM environment
  2. Starting a VM
  3. Register native interfaces of Android, including Binder, OpenGL, Bitmap, Camera, AudioRecord, etc
  4. Find ZygoteInit, pass the arguments, and call its main method, which blocks until the virtual machine exits
  5. Destroy related resources when the VM exits

Virtual machine related processes ignore, assuming the VM starts successfully, then com. Android. Internal. OS. ZygoteInit (the package name is app_main CPP files coming) of the main method will be invoked, Zygote is thought to have been started as well, meaning Zygote was started when the first Android process init parsed the file init.rc. The Android virtual machine is started first, followed by Zygote, whose main function blocks execution until the virtual machine exits.

How does Zygote incubate the daughter process?

ZygoteInit’s main method:

/**
* Startup class forthe zygote process. */ public class ZygoteInit { public static void main(String argv[]) { ZygoteServer zygoteServer = new ZygoteServer(); // Create Zygote Server Socket object... final Runnablecaller;
        try {
            ...

            boolean startSystemServer = false;
            String socketName = "zygote";
            String abiList = null;
            boolean enableLazyPreload = false;
            for (int i = 1; i < argv.length; i++) {
                if ("start-system-server".equals(argv[I])) {// init.rc specifies the argument --start-system-server startSystemServer =true;
                } else if ("--enable-lazy-preload".equals(argv[i])) {
                    ...
                } else{... }} / / registered Socket zygoteServer. RegisterServerSocket (socketName);if (!enableLazyPreload) {// Preload (bootTimingsTraceLog) public resources required by all applications; }else{ Zygote.resetNicePriority(); }...if(startSystemServer) {// fork a SystemServer process Runnable r = forkSystemServer(abiList, socketName, zygoteServer); // {@code r == null}inthe parent (zygote) process, and {@code r ! = null}in the
                // child (system_server) process.
                if(r ! = null) { r.run();return;
                }
            }

            // The select loop returns early in the child process after a fork and
            // loops forever inThe zygote. // The Zygote process waits for connections on an infinite loopcaller = zygoteServer.runSelectLoop(abiList);
        } catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex); throw ex; } finally { zygoteServer.closeServerSocket(); // Zygote exits the loop, closes the Socket} // 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

As you can see from the code, ZygoteInit does four things:

  1. Register a Socket. Zygote is an incubator through which the system notifies it of a process whenever a new application needs to run
  2. Preloads various resources
  3. Start the SystemServer. Zygote is initialized only once, so it needs to create a special process to host the running of the system services
  4. In an endless loop, Socket requests are continuously processed

As you can see, Zygote handles client requests mainly through sockets. The above tasks will be analyzed one by one below.

Registration of the Socket

class ZygoteServer {

    private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";

    private LocalServerSocket mServerSocket;

    void registerServerSocket(String socketName) {
        if(mServerSocket == null) { int fileDesc; final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName; / / socketName for"zygote"
            try {
                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); mServerSocket = new LocalServerSocket(fd); // create Server Socket} catch (IOException ex) {throw new RuntimeException("Error binding to local socket '" + fileDesc + "'", ex); }}}}Copy the code

LocalServerSocket is constructed as follows:

/**
* Non-standard class for creating an inbound UNIX-domain socket
* in the Linux abstract namespace.
*/
public class LocalServerSocket {

    private final LocalSocketImpl impl;
    private final LocalSocketAddress localAddress; public LocalServerSocket(String name) throws IOException { impl = new LocalSocketImpl(); // Socket implementation class impl. Create (localsocket.socket_stream); / / TCP connectionlocalAddress = new LocalSocketAddress(name);
        impl.bind(localAddress); / / bind impl. Listen (LISTEN_BACKLOG); // listen}}Copy the code

As you can see, registerServerSocket creates a Socket object named “ANDROID_SOCKET_zygote” and binds the corresponding port to start listening for requests from other processes. The Socket is a UNIX-Domain Socket, which is used for interprocess communication within a single device. Binder aside, This Socket is the most common way for Android to implement interprocess communication.

preload

Here’s a quick look at the preload method:

public class ZygoteInit {

    static void preload(TimingsTraceLog bootTimingsTraceLog) {
        Log.d(TAG, "begin preload");
        beginIcuCachePinning();
        
        preloadClasses();
        
        preloadResources();
        
        nativePreloadAppProcessHALs();
        
        preloadOpenGL();
        
        preloadSharedLibraries();
        
        preloadTextResources();
        
        // Ask the WebViewFactory to do any initialization that must run in the zygote process,
        // for memory sharing purposes.
        WebViewFactory.prepareWebViewInZygote();
        
        endIcuCachePinning();
        
        warmUpJcaProviders();
        Log.d(TAG, "end preload");

        sPreloadComplete = true; }}Copy the code

As you can see, preload mainly preloads various classes, HAL layer resources, OpenGL environment, shared libraries, text resources, WebView resources, etc.

forkSystemServer

ForkSystemServer is used to start the SystemServer process. SystemServer is a particularly key class in the Android Framework for starting various system services:

public class ZygoteInit {

    /**
     * Prepare the arguments and forks for the system server process.
     *
     * Returns an {@code Runnable} that provides an entrypoint into system_server code in the
     * child process, and {@code null} in the parent.
     */
    private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        
        ...
        
        /* Hardcoded command line to start the system server */
        String args[] = {
            "--setuid=1000"."--setgid=1000"."- setgroups = 1001100 2100 3100 4100 5100 6100 7100 8100 9101 0101 8102 1102 3103 2300 1300 2300 3300 6300 7300 9301 0"."--capabilities=" + capabilities + "," + capabilities,
            "--nice-name=system_server"."--runtime-args"."com.android.server.SystemServer"}; ZygoteConnection.Arguments parsedArgs = null; int pid; Try {/ / compile parameters to the ZygoteConnection parsedArgs = new ZygoteConnection. The Arguments (args); ZygoteConnection.applyDebuggerSystemProperty(parsedArgs); ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs); /* Request to fork the system server process */ * Request to fork the system server process */ parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, null, parsedArgs.permittedCapabilities, parsedArgs.effectiveCapabilities); } catch (IllegalArgumentException ex) { throw new RuntimeException(ex); } /* For child process */ /if (pid == 0) { 
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }

            zygoteServer.closeServerSocket();
            returnhandleSystemServerProcess(parsedArgs); // Start SystemServer}returnnull; }}Copy the code

As you can see, the first forkSystemServer through a Zygote fork process, then call handleSystemServerProcess to start SystemServer. Zygote is implemented as follows:

public final class Zygote { /** * Special method to start the system server process. */ public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) { ... int pid = nativeForkSystemServer( uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities); .returnpid; }}Copy the code

The fork process is implemented in native code:

// com_android_internal_os_Zygote.cpp

static const char kZygoteClassName[] = "com/android/internal/os/Zygote"; static jclass gZygoteClass; static jmethodID gCallPostForkChildHooks; static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(...) {...return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags,
            rlimits, capabilities, capabilities, mount_external, se_info,
            se_name, false, fdsToClose, fdsToIgnore, instructionSet, appDataDir); } // Utility routine to fork zygote and specialize the child process. static pid_t ForkAndSpecializeCommon(...) {... pid_t pid = fork(); // A new process is actually forked hereif(pid == 0) { ... // Call back Zygote's Callpostforkchildhood, Notify the VM child process that it is created env->CallStaticVoidMethod(gZygoteClass, gCallPostforkchildhood, debug_Flags, is_system_server, instructionSet); }else if(pid > 0) {// Parent process does nothing}return pid;
}
Copy the code

As you can see, the native code calls the Linux fork function, as a new process is created, see below handleSystemServerProcess:

public class ZygoteInit {

    /**
     * Finish remaining work for* The newly forked system server process is newly forked by forkSystemServer. * The newly forked system server process is newly forked by forkSystemServer. Contains the package name * / SystemServer private static Runnable handleSystemServerProcess (ZygoteConnection. The Arguments parsedArgs) {...if(parsedArgs.invokeWith ! = null) {// Compare forkSystemServer with null... }else {
            ClassLoader cl = null;
            if(systemServerClasspath ! Cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion); Thread.currentThread().setContextClassLoader(cl); } /* * Pass the remaining arguments to SystemServer. */return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
        }
    }
    
    public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
        ...
        ZygoteInit.nativeZygoteInit();
        return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
    }
    
    private static final native void nativeZygoteInit();
    
}
Copy the code

HandleSystemServerProcess mainly do three things:

  1. Find the ClassLoader corresponding to SystemServer
  2. Call nativeZygoteInit
  3. Call RuntimeInit applicationInit

Here’s nativeZygoteInit:

// framework/base/core/jni/AndroidRuntime.cpp
static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{
    gCurRuntime->onZygoteInit();
}
Copy the code

OnZygoteInit and other callbacks are implemented by the AndroidRuntime subclass AppRuntime:

class AppRuntime : public AndroidRuntime
{
public:
    ...
    virtual void onZygoteInit() { sp<ProcessState> proc = ProcessState::self(); proc->startThreadPool(); }}Copy the code

StartThreadPool will open Binder thread pool to ensure that other processes can the services provided by the right to access to the Zygote, startThreadPool source implementation of the skip, continue watching RuntimeInit… applicationInit:

public class RuntimeInit {

    protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
            ClassLoader classLoader) {
        ...
        // Remaining arguments are passed to the start class's static main return findStaticMain(args.startClass, args.startArgs, classLoader); } private 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 (NoSuchMethodException ex) {// Catch (NoSuchMethodException ex) {... } return new MethodAndArgsCaller(m, argv); } 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() { try { mMethod.invoke(null, new Object[] { mArgs }); Catch (IllegalAccessException ex) {// Call SystemServer main} catch (IllegalAccessException ex) {... }}}}Copy the code

As you can see, applicationInit returns a Runnable that calls SystemServer’s main method. This Runnable will be called in ZygoteInit’s main method. That is, the Runnable object returned by applicationInit is immediately executed, at which point SystemServer’s main method is called, and many system services are started.

Here is a summary of what forkSystemServer does:

  1. Fork a new process
  2. Find the class loader for SystemServer
  3. Start SystemServer by calling SystemServer’s main method

runSelectLoop

ForkSystemServer is done with the runSelectLoop:

class ZygoteServer {

    private LocalServerSocket mServerSocket;

    /**
     * Runs the zygote process's select loop. Accepts new connections as * they happen, and reads commands from connections one spawn-request's
     * worth at a time.
     */
    Runnable runSelectLoop(String abiList) {
        ...
        while (true) {...for (int i = pollFds.length - 1; i >= 0; --i) {
                if ((pollFds[i].revents & POLLIN) == 0) {
                    continue;
                }

                if (i == 0) {
                    ...
                } else {
                    try {
                        ZygoteConnection connection = peers.get(i);
                        final Runnable command = connection.processOneCommand(this);

                        if(mIsForkChild) {// If the current is a child // We're in the child. We should always have a command to run at this // stage if processOneCommand hasn't called "exec". // ExecutingexecCommand before the child processcommandIt should not be nullif (command == null) {
                                throw new IllegalStateException("command == null");
                            }
                            return command;
                        } else{// parent process // We're in the server - we should never have any commands to run. // There should be no commands to run in the server. = 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 inOur regular processing loop. // If the connection is closed, remove the resourceif (connection.isClosedByPeer()) {
                                connection.closeSocket();
                                peers.remove(i);
                                fds.remove(i);
                            }
                        }
                    } catch (Exception e) {
                        ...
                    }
                }
            }
        }
    }
    
}
Copy the code

As you can see, the runSelectLoop is an infinite loop that keeps fetching ZygoteConnection and executing the corresponding command until an exception occurs or the connection is closed. Here’s how it executes the command:

class ZygoteConnection { Runnable processOneCommand(ZygoteServer zygoteServer) { ... // select * from process; This method is identical to the forkSystemServer (Zygote. ForkAndSpecialize (Parsedargs.uid, Parsedargs.gid, Parsedargs.gids). parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet, parsedArgs.appDataDir); try {if (pid == 0) {
                // inChild tags for the child process zygoteServer. SetForkChild (); zygoteServer.closeServerSocket(); serverPipeFd = null; // The newly created process needs to run the application's own codereturn handleChildProc(parsedArgs, descriptors, childPipeFd);
            } else{... HandleParentProc (PID, descriptors, serverPipeFd) handleParentProc(PID, descriptors, serverPipeFd) handleParentProc(PID, descriptors, serverPipeFd)returnnull; } } finally { ... }}}Copy the code

Take a quick look at handleChildProc:

class ZygoteConnection {

    /**
     * Handles post-fork setup of child proc, closing sockets as appropriate,
     * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
     * if successful or returning iffailed. */ private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, FileDescriptor pipeFd) { closeSocket(); .if(parsedArgs.niceName ! = null) { Process.setArgV0(parsedArgs.niceName); }if(parsedArgs.invokeWith ! = null) { ... // splice init.rc command to start the corresponding service}else{// previously analyzedreturnZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, null /* classLoader */); }}}Copy the code

As you can see, handleChildProc, like handleSystemServerProc, ends up calling zygoteinit.zygoteinit to start a Binder thread pool and execute the main method of the corresponding class.

To recap, the runSelectLoop is used to continuously process requests from the Socket. For each request, Zygote forks a process, opens a Binder thread pool so that the child process can interact with the system process, and finally calls the main method of the specified class — in fact, when a new Application starts, The main method of ActivityThread is called.

From Android virtual machine startup, to Zygote startup, to application process startup, ActivityThread execution, the line is basically connected.

conclusion

Some key points in the connection:

  1. The Android VM and Zygote are started by the init process by parsing init.rc, which specifies Zygote and SystemServer parameters
  2. After the Android VIRTUAL machine successfully starts, ZygoteInit’s main method is called, which blocks execution until the virtual machine exits
  3. ZygoteInit’s main method first registers a UNIX-Domain Socket to listen for requests from clients
  4. Then preload all kinds of resources, including HAL layer resources, OpenGL environment, various classes, etc
  5. Then fork a process to run SystemServer and call SystemServer’s main method to start various services
  6. Finally, an endless loop continues to process Socket requests
  7. When a new request comes in, Zygote forks a process, opens the Binder thread pool so that the child process can interact with the system process, and finally calls ActivityThread’s main method

Flow chart: