Zygote

Zygote is a special process that acts as an incubator for new application processes. Zygote is launched when a user launches a new application and is responsible for creating and incubating new application processes.

The following is the basic process of Zygote startup:

  1. System startup : When the Android device starts, the system loads the Zygote executable file into the memory and starts it as a system service.
  2. Initialization process : Zygote performs a series of initialization operations after startup, including creating virtual machine (VM) instances, loading system classes and resources, etc.
  3. Waiting for new requests : Zygote enters a loop waiting state, listening for requests from the system and application managers.
  4. Receiving application requests : When a user starts a new application, the application manager sends a request to Zygote to create a new application process.
  5. Copy itself : Upon receiving a request to create a new application process, Zygote creates a child process similar to itself using the fork() system call.
  6. Application process incubation : In the child process, Zygote will perform some necessary operations, such as setting the application’s environment variables, loading the application’s code and resources, etc.
  7. Application Startup : Once the incubation process is complete, Zygote starts the new application process by calling the application’s entry point.

detailed steps:

1) Create a Java virtual machine and register native methods for the Java virtual machine;

2) Call the main method of the java class in com.android.internal.os.ZygoteInit;

3) Load ZygoteInit class;

4) Register zygote socket;

5) Load preload class;/system/etc/preloaded-classes

6) Load the preload resource file;

7) Call Zygote::forkSystemServer, fork a new process, call the main method of SystemServer, and enter the initialization process of the system_server process of the java layer;

In this way, Zygote can reduce application startup time because it preloads commonly used classes and resources and provides a clean copy of the application process. This way, Zygote only needs to copy itself and load the application-specific parts each time the application is launched, rather than having to recreate the entire process. This incubation mechanism helps improve application responsiveness and system performance.

After Zygote is started -> main method of entry app_main.cpp: The entry function calls AndroidRuntime’s startVM() method to create a virtual machine; after calling startReg() to register the JNI function, call ZygoteInit.main() through JNI. Register socket for preloading and fork systemserver process

init.zygote64.rc:

init.zygote64 is an init script used to set up and start the 64-bit version of the Zygote process during startup. Specifically, the functions of the init.zygote64 script include the following aspects:

  1. Environment initialization : The init.zygote64 script is responsible for initializing the environment of the Zygote process, including setting environment variables, loading system properties and other operations.
  2. Start the Zygote process : This script will start the 64-bit version of the Zygote process to prepare for subsequent application incubation.
  3. Perform pre-start operations : init.zygote64 may perform some pre-start operations, such as preloading some commonly used class libraries and resources, to speed up the startup of the application.
  4. Set permissions and users : init.zygote64 may set the permissions and user of the Zygote process to ensure that the process can run normally and has sufficient permissions.

In general, the init.zygote64 script plays an important role in the Android system startup process. It is responsible for starting and initializing the 64-bit version of the Zygote process and providing the necessary environment and preparations for subsequent application startup.

\build\android\system\core\rootdir\init.zygote64.rc

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary 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
    onrestart restart wificond
    onrestart restart vendor.servicetracker-1-1
    writepid /dev/cpuset/foreground/tasks
​

AndroidRuntime.cpp:

\build\android\frameworks\base\core\jni\AndroidRuntime.cpp

Start java virtual machine

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ALOGD(">>>>>> START %s uid %d <<<<<<\n",
            className != NULL ? className : "(unknown)", getuid());
​
    static const String8 startSystemServer("start-system-server");
​
    /*
     * 'startSystemServer == true' means runtime is obsolete and not run from
     * init.rc anymore, so we print out the boot start event here.
     */
    for (size_t i = 0; i < options.size(); ++i) {
        if (options[i] == startSystemServer) {
           /* 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)));   // 
        }
    }
​
    const char* rootDir = getenv("ANDROID_ROOT");
    if (rootDir == NULL) {
        rootDir = "/system";
        if (!hasDir("/system")) {
            LOG_FATAL("No root directory specified, and /system does not exist.");
            return;
        }
        setenv("ANDROID_ROOT", rootDir, 1);
    }
​
    const char* runtimeRootDir = getenv("ANDROID_RUNTIME_ROOT");
    if (runtimeRootDir == NULL) {
        LOG_FATAL("No runtime directory specified with ANDROID_RUNTIME_ROOT environment variable.");
        return;
    }
​
    const char* tzdataRootDir = getenv("ANDROID_TZDATA_ROOT");
    if (tzdataRootDir == NULL) {
        LOG_FATAL("No tz data directory specified with ANDROID_TZDATA_ROOT environment variable.");
        return;
    }
​
    //const char* kernelHack = getenv("LD_ASSUME_KERNEL");
    //ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
​
    /* start the virtual machine */             // 
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);                         
​
    /*
     * Register android functions.             // 
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
​
    /*
     * We want to call main() with a String array with arguments in it.
     * At present we have two arguments, the class name and an option string.
     * Create an array to hold them.
     */
    jclass stringClass;                    // 
    jobjectArray strArray;
    jstring classNameStr;
    
    stringClass = env->FindClass("java/lang/String");
    assert(stringClass != NULL);
    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
    assert(strArray != NULL);
    classNameStr = env->NewStringUTF(className);
    assert(classNameStr != NULL);
    env->SetObjectArrayElement(strArray, 0, classNameStr);
​
    for (size_t i = 0; i < options.size(); ++i) {
        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
        assert(optionsStr != NULL);
        env->SetObjectArrayElement(strArray, i + 1, optionsStr);
    }
​
    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        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 {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
​
#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }
    free(slashClassName);
​
    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");
}

app_main.cpp:

\build\android\frameworks\base\cmds\app_process\app_main.cpp

int main(int argc, char* const argv[])
{
   std::string bootmode = GetProperty("ro.bootmode", "");
​
   if ((strncmp(bootmode.c_str(), "ffbm-00", 7) == 0)
           || (strncmp(bootmode.c_str(), "ffbm-01", 7) == 0)) {
           return 0;
   }
​
   if (!LOG_NDEBUG) {
     String8 argv_String;
     for (int i = 0; i < argc; ++i) {
       argv_String.append(""");
       argv_String.append(argv[i]);
       argv_String.append("" ");
     }
     ALOGV("app_process main with argv: %s", argv_String.string());
   }
​
   AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
   // Process command line arguments
   // ignore argv[0]
   argc--;
   argv++;
​
   // Everything up to '--' or first non '-' arg goes to the vm.
   //
   // The first argument after the VM args is the "parent dir", which
   // is currently unused.
   //
   // After the parent dir, we expect one or more the following internal
   // arguments :
   //
   // --zygote : Start in zygote mode
   // --start-system-server : Start the system server.
   // --application : Start in application (stand alone, non zygote) mode.
   // --nice-name : The nice name for this process.
   //
   // For non zygote starts, these arguments will be followed by
   // the main class name. All remaining arguments are passed to
   // the main method of this class.
   //
   // For zygote starts, all remaining arguments are passed to the zygote.
   // main function.
   //
   // Note that we must copy argument string values since we will rewrite the
   // entire argument block when we apply the nice name to argv0.
   //
   // As an exception to the above rule, anything in "spaced commands"
   // goes to the vm even though it has a space in it.
   const char* spaced_commands[] = { "-cp", "-classpath" };
   // Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
   bool known_command = false;
​
   int i;
   for (i = 0; i < argc; i++) {
       if (known_command == true) {
         runtime.addOption(strdup(argv[i]));
         // The static analyzer gets upset that we don't ever free the above
         // string. Since the allocation is from main, leaking it doesn't seem
         // problematic. NOLINTNEXTLINE
         ALOGV("app_process main add known option '%s'", argv[i]);
         known_command = false;
         continue;
       }
​
       for (int j = 0;
            j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
            ++j) {
         if (strcmp(argv[i], spaced_commands[j]) == 0) {
           known_command = true;
           ALOGV("app_process main found known command '%s'", argv[i]);
         }
       }
​
       if (argv[i][0] != '-') {
           break;
       }
       if (argv[i][1] == '-' && argv[i][2] == 0) {
           ++i; // Skip --.
           break;
       }
​
       runtime.addOption(strdup(argv[i]));
       // The static analyzer gets upset that we don't ever free the above
       // string. Since the allocation is from main, leaking it doesn't seem
       // problematic. NOLINTNEXTLINE
       ALOGV("app_process main add option '%s'", argv[i]);
   }
​
   // 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) {
           zygote = true;
           niceName = ZYGOTE_NICE_NAME;
       } else if (strcmp(arg, "--start-system-server") == 0) {
           startSystemServer = true;
       } else if (strcmp(arg, "--application") == 0) {
           application = true;
       } else if (strncmp(arg, "--nice-name=", 12) == 0) {
           niceName.setTo(arg + 12);
       } else if (strncmp(arg, "--", 2) != 0) {
           className.setTo(arg);
           break;
       } else {
           --i;
           break;
       }
   }
​
   Vector<String8> args;
   if (!className.isEmpty()) {
       // We're not in zygote mode, the only argument we need to pass
       // to RuntimeInit is the application argument.
       //
       // The Remainder of args get passed to startup class main(). Make
       // copies of them before we overwrite them with the process name.
       args.add(application ? String8("application") : String8("tool"));
       runtime.setClassNameAndArgs(className, argc - i, argv + i);
​
       if (!LOG_NDEBUG) {
         String8 restOfArgs;
         char* const* argv_new = argv + i;
         int argc_new = argc - i;
         for (int k = 0; k < argc_new; ++k) {
           restOfArgs.append(""");
           restOfArgs.append(argv_new[k]);
           restOfArgs.append("" ");
         }
         ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());
       }
   } else {
       // We're in zygote mode.
       maybeCreateDalvikCache();
​
       if (startSystemServer) {
           args.add(String8("start-system-server"));
       }
​
       char prop[PROP_VALUE_MAX];
       if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
           LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
               ABI_LIST_PROPERTY);
           return 11;
       }
​
       String8 abiFlag("--abi-list=");
       abiFlag.append(prop);
       args.add(abiFlag);
​
       // In zygote mode, pass all remaining arguments to the zygote
       // main() method.
       for (; i < argc; ++i) {
           args.add(String8(argv[i]));
       }
   }
​
   if (!niceName.isEmpty()) {
       runtime.setArgv0(niceName.string(), true /* setProcName */);
   }
​
   if (zygote) {            // 
       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.");
   }
}

ZygoteInit.java:

\build\android\frameworks\base\core\java\com\android\internal\os\ZygoteInit.java

1. Call preload() to preload classes and resources. 2. Call ZygoteServer() to create two server-side Sockets to wait for ActivityManagerService to request Zygote to create a new application process. 3. Call forkSystemServer to start the SystemServer process 4. Finally call the runSelectLoop function to wait for the client to request the Zygote process. Through the fork() function, the system_server process is finally hatched and started through reflection.

Zygote itself is a Native application. The name of the process at the beginning is “app_process”. During the running process, the name is changed to zygote or zygote64 (depending on the operating system) by calling setArgv0, and finally the start() method of the runtime is used. Load the virtual machine and enter the JAVA program

Preloading to speed up app startup:

static void preload(TimingsTraceLog bootTimingsTraceLog) {
       Log.d(TAG, "begin preload");
       bootTimingsTraceLog.traceBegin("BeginPreload");
       beginPreload();
       bootTimingsTraceLog.traceEnd(); // BeginPreload
       bootTimingsTraceLog.traceBegin("PreloadClasses");
       preloadClasses();
       bootTimingsTraceLog.traceEnd(); // PreloadClasses
       bootTimingsTraceLog.traceBegin("CacheNonBootClasspathClassLoaders");
       cacheNonBootClasspathClassLoaders();
       bootTimingsTraceLog.traceEnd(); // CacheNonBootClasspathClassLoaders
       bootTimingsTraceLog.traceBegin("PreloadResources");
       preloadResources();
       bootTimingsTraceLog.traceEnd(); // PreloadResources
       Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadAppProcessHALs");
       nativePreloadAppProcessHALs();
       Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
       Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadGraphicsDriver");
       maybePreloadGraphicsDriver();
       Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
       preloadSharedLibraries();
       preloadTextResources();
       // Ask the WebViewFactory to do any initialization that must run in the zygote process,
       // for memory sharing purposes.
       WebViewFactory.prepareWebViewInZygote();
       endPreload();
       warmUpJcaProviders();
       Log.d(TAG, "end preload");
​
       sPreloadComplete = true;
   }

main():

1. Call preload() to preload classes and resources. 2. Call ZygoteServer() to create two server-side Sockets to wait for ActivityManagerService to request Zygote to create a new application process. 3. Call forkSystemServer to start the SystemServer process 4. Finally call the runSelectLoop function to wait for the client to request the Zygote process. Through the fork() function, the system_server process is finally hatched and started through reflection.

public static void main(String argv[]) {
       ZygoteServer zygoteServer = null;
​
       // Mark zygote start. This ensures that thread creation will throw
       // an error.
       ZygoteHooks.startZygoteNoThreadCreation();
​
       // Zygote goes into its own process group.
       try {
           Os.setpgid(0, 0);
       } catch (ErrnoException ex) {
           throw new RuntimeException("Failed to setpgid(0,0)", ex);
       }
​
       Runnable caller;
       try {
           // Report Zygote start time to tron unless it is a runtime restart
           if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {
               MetricsLogger.histogram(null, "boot_zygote_init",
                       (int) SystemClock.elapsedRealtime());
           }
​
           String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";
           TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
                   Trace.TRACE_TAG_DALVIK);
           bootTimingsTraceLog.traceBegin("ZygoteInit");
           RuntimeInit.enableDdms();
​
           boolean startSystemServer = false;
           String zygoteSocketName = "zygote";
           String abiList = null;
           boolean enableLazyPreload = false;
           for (int i = 1; i < argv.length; i++) {
               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)) {
                   zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
               } else {
                   throw new RuntimeException("Unknown command line argument: " + argv[i]);
               }
           }
​
           final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);
​
           if (abiList == null) {
               throw new RuntimeException("No ABI list supplied.");
           }
​
           // In some configurations, we avoid preloading resources and classes eagerly.
           // In such cases, we will preload things prior to our first fork.
           if (!enableLazyPreload) {
               bootTimingsTraceLog.traceBegin("ZygotePreload");
               EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                       SystemClock.uptimeMillis());
               preload(bootTimingsTraceLog);
               EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                       SystemClock.uptimeMillis());
               bootTimingsTraceLog.traceEnd(); // ZygotePreload
           } else {
               Zygote.resetNicePriority();
           }
​
           // Do an initial gc to clean up after startup
           bootTimingsTraceLog.traceBegin("PostZygoteInitGC");
           gcAndFinalize();
           bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC
​
           bootTimingsTraceLog.traceEnd(); // ZygoteInit
           // Disable tracing so that forked processes do not inherit stale tracing tags from
           // Zygote.
           Trace.setTracingEnabled(false, 0);
​
​
           Zygote.initNativeState(isPrimaryZygote);
​
           ZygoteHooks.stopZygoteNoThreadCreation();
​
           zygoteServer = new ZygoteServer(isPrimaryZygote);
​
           if (startSystemServer) {
               Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
​
               // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
               // child (system_server) process.
               if (r != null) {
                   r.run();
                   return;
               }
           }
​
           Log.i(TAG, "Accepting command socket connections");
​
           // The select loop returns early in the child process after a fork and
           // loops forever in the zygote.
           caller = zygoteServer.runSelectLoop(abiList);
       } catch (Throwable ex) {
           Log.e(TAG, "System zygote died with exception", ex);
           throw ex;
       } finally {
           if (zygoteServer != null) {
               zygoteServer.closeServerSocket();
           }
       }

Zygote incubates application processes

Leave a Reply

Your email address will not be published. Required fields are marked *