In the last article we looked at the startup process of init. One of the three main things that happens during startup is that a Zygote process is created. So what is a Zygote process and what does it do?
I. Introduction to Zygote
In Android, the DVM(Dalvik Virtual Machine), the application process, and the SystemServer process that runs the system’s key services are all created by the Zygote process, which we also call the incubator. It forkes the application process and SystemServer process, and since Zygote creates a DVM at startup, the application process and SystemServer process forked can acquire an instance copy of the DVM internally.
2. AppRuntime analysis
CPP > init > app_main. CPP > app_main. CPP > app_main. CPP > app_main. CPP > app_main. CPP > app_main. CPP > app_main. CPP > app_main. CPP
int main(int argc, char* const argv[])
{
...
while(i < argc) { const char* arg = argv[i++]; / / 1if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
// 2
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
// 3
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; }}... / / 4if (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."); }}Copy the code
The Zygote process forks itself to create a child process. In this way, the Zygote process and its children fork into the main function of app_main. CPP. In the mian function, the Zygote process is used to determine which process is running. Arg determines that the arG release contains “-zygote”, if it does, then main is running in a Zygote process and sets the zygote flag to true. In comment 2, the arg parameter is checked to see if it contains “– start-system-server”. If it does, it indicates that it is currently in the SystemServer process and startSystemServer is set to true. Similarly, comment 3 checks whether the arg parameter contains “– application”, if it does, indicating that it is currently in the application process and sets the application flag to true. Finally, in comment 4, the zygote process is started using appruntime.start () when the zygote flag is true, that is, when the zygote process is currently in progress.
Let’s look at the AndroidRuntime start function:
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) { ... /* start the virtual machine */ JniInvocation jni_invocation; jni_invocation.Init(NULL); JNIEnv* env; / / 1if(startVm(&mJavaVM, &env, zygote) ! = 0) {return; } onVmCreated(env); /* * 2, Register android functions. */if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
...
// 3
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 * notreturnuntil the VM exits. */ // 4 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 {
// 6
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 {
// 6
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif} } free(slashClassName); . }Copy the code
The startVm function is used in comment 1 to start the Java VIRTUAL machine, and the startReg function is used in comment 2 to register JNI methods for the Java virtual machine. In the annotations of 3 classNameStr is the incoming parameters, value of com. Android. Internall. OS. ZygoteInit. Then use the toSlashClassName function in comment 4 to pass the “of className. Replace with “/” to replace the value of the com/android/internal/OS/ZygoteInit. We then locate ZygoteInit based on this value and find ZygoteInit’s main function in comment 5. Finally, we call ZygoteInit’s main function in comment 6 using JNI. We use JNI here because ZygoteInit is Java code. Eventually, Zygote moved from Native to the Java FrameWork layer. Before that, no code had entered the Java FrameWork layer, so Zygote can be considered the beginning of the Java FrameWork layer.
Next, let’s look at the main method in zygoteinit.java:
public static void main(String argv[]) {
...
try {
...
// 1
zygoteServer.registerServerSocketFromEnv(socketName);
// 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());
// 2
preload(bootTimingsTraceLog);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd(); // ZygotePreload
} else{ Zygote.resetNicePriority(); }...if (startSystemServer) {
// 3
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;
}
}
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.
// 4
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
} finally {
zygoteServer.closeServerSocket();
}
// 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
First of all, in the note 1 call the ZygoteServer registerServerSocketFromEnv method to create a Server side called “zygote” Socket, It is used to wait for ActivityManagerService to request Zygote to create a new application process. I first analyze the registerServerSocketFromEnv method processing logic, the source code is as follows:
private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
void registerServerSocketFromEnv(String socketName) {
if (mServerSocket == null) {
int fileDesc;
// 1
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
// 2
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);
// 3
mServerSocket = new LocalServerSocket(fd);
mCloseSocketFd = true;
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex); }}}Copy the code
First, the Socket name is concatenated to “ANDROID_SOCKET_zygote” at comment 1, and the system.getenv () method is called at comment 2 to get the value of the Socket’s environment variable. The Socket environment variable value is then resolved to a file descriptor parameter of type int. Next, in comment 4, get a file descriptor using the file descriptor argument obtained above and create a new server Socket from it. When Zygote starts SystemServer, it waits on the server Socket for AMS to request Zygote to create a new application process.
Next, we go back to ZygoteInit’s main method, which preloads the classes and resources in comment 2. Then, in comment 3, the forkSystemServer() method is used to create the SystemServer process. The forkSystemServer() method core code is as follows:
private static Runnable forkSystemServer(String abiList, String socketName, ZygoteServer ZygoteServer ZygoteServer ZygoteServer ZygoteServer ZygoteServer ZygoteServer ZygoteServer ZygoteServer ZygoteServer ZygoteServer /* Request to fork the system server process */ / 3.1 pid = Zygote. ForkSystemServer (parsedargs.uid, parsedargs.gid, parsedArgs.gids, parsedArgs.runtimeFlags, 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(); / / 3.2return handleSystemServerProcess(parsedArgs);
}
return null;
}
Copy the code
As you can see, the forkSystemServer() method in note 3.1 calls Zygote’s forkSystemServer() method to create the SystemServer process, which internally executes the nativeForkSystemServer Native method. It ends up using the fork function to create a SystemServer process in the current process. If pid is zero, that is, the current is in the newly created the child ServerServer process, in the notes 3.2 using handleSystemServerProcess SystemServer () method of the processing process of some of the work.
Back to zygoteinit.java in comment 4 of the main method, where ZygoteServer’s runSelectLoop method is called to wait for ActivityManagerService to request the creation of a new application process, The runSelectLoop() method looks like this:
Runnable runSelectLoop(String abiList) { ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>(); ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>(); // 1 fds.add(mServerSocket.getFileDescriptor()); peers.add(null); // 2, wait indefinitely for AMS request to create application processwhile (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex); } / / 3for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue; } / / 4if (i == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
try {
ZygoteConnection connection = peers.get(i);
// 5
final Runnable command = connection.processOneCommand(this);
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");
}
return command;
} else {
// We're in the server - we should never have any commands to run. if (command ! = 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 in our regular processing loop.
if (connection.isClosedByPeer()) {
connection.closeSocket();
peers.remove(i);
fds.remove(i);
}
}
} catch (Exception e) {
if(! mIsForkChild) { // We're in the server so any exception here is one that has taken place // pre-fork while processing commands or reading / writing from the // control socket. Make a loud noise about any such exceptions so that // we know exactly what failed and why. Slog.e(TAG, "Exception executing zygote command: ", e); // Make sure the socket is closed so that the other end knows immediately // that something has gone wrong and doesn't time out waiting for a
// response.
ZygoteConnection conn = peers.remove(i);
conn.closeSocket();
fds.remove(i);
} else {
// We're in the child so any exception caught here has happened post // fork and before we execute ActivityThread.main (or any other main() // method). Log the details of the exception and bring down the process. Log.e(TAG, "Caught post-fork exception in child process.", e); throw e; } } finally { // Reset the child flag, in the event that the child process is a child- // zygote. The flag will not be consulted this loop pass after the Runnable // is returned. mIsForkChild = false; } } } } }Copy the code
First, in comment 1, the getFileDescriptor() function on the server side of mServerSocket is called to get its fd field value and add it to the FDS list. Then, in comment 2, an infinite loop is used to wait for AMS to request the Zygote process to create a new application process. The fd list pollFds is iterated in comment 3. If I equals 0, the server Socket is connected to the client, that is, the Zygote process is connected to the AMS process. Next, call the acceptCommandPeer() method at comment 4 to get the ZygoteConnection object and add it to the list of peers. If I is not equal to 0, AMS wants the Zygote process to send a request to create an application process, and the zygoteconnection.runonce method is executed at comment 5 to create a new application process.
conclusion
From the above analysis, it can be seen that the main responsibilities of Zygote process startup are as follows:
- Create e AppRuntime and execute its start method to start the Zygote process.
- Create the JVM and register JNI methods for the JVM.
- Use JNI to call ZygoteInit’s main function to enter the Java FrameWork layer of Zygote.
- The registerZygoteSocket method is used to create server-side sockets, and the runSelectLoop method and other AMS requests are used to create new applications.
- Start the SystemServer process.