In the init process that started in the previous chapter, the last thing you need to do is parse the init.rc file, and starting with Android 8.0, init.rc is split into different init.xxx.rc files for different services, one of which is Zygote.

Zygote

The Zygote process is created when init is created. The initial name of the Zygote process is not Zygote, but “app_process”, which is defined in Android.mk. The PCTRL system on Linux calls app_process, replacing its name with zygote. So that’s how Zygote got its name. In The Android operating system, the DVM and ART, the application process, and the key service SystemServer process that runs the system are all fork from the Zygote process. When Zygote process is created, it creates a DVM or ART. So the Zygote fork process can obtain an internal DVM or ART instance copy, the implementation of the process with its own JVM, so Android system can be embedded in different types of devices, because Linux + JVM can be very good cross-platform support.

Start the process

Init process The first process created is the Zygote process. The corresponding file name is init.zygotexx. rc startup script file. XX is the processor bit. For example, a 64-bit processor is init.zygote64.rc. The actual reference code in init.rc is as follows:

import /init.${ro.zygote}.rc
Copy the code

Ro.zygote is a system property, and the init.rc file introduces different files based on the contents of the property.

Since Android5.0, Android has started to support 64-bit applications, Zygote has 32 and 64 bit difference, ro. Zygote has the following four values:

  • Init. zygote32.rc: pure 32-bit programs are supported.
  • Init.zygote64. rc: indicates that pure 64-bit programs are supported.
  • Init. zygote32_64.rc: indicates that both 32-bit and 64-bit programs are supported. There are two services in the script, which will initiate two Zygote processes. One named Zygote will execute app_process32 as the main mode and the other named Zygot ę_secondary will execute app_process64. As a secondary mode. Init.zygote64_32.rc: Primary and secondary modes are exactly the opposite of 32_64.

The startup script creates a Service based on the parameters, and Zygote is a Service. After a Service is created, it is stored in a vector of services. After the Service is created, the Service is started by calling the class_start method.

The actual equivalent of class_start is the do_class_start function. The StartIfNotDisabled method of the Service is called in do_class_start:

In the StartIfNotDisabled method, only the Start method is called once, and the pseudocode logic for this method is:

  1. If the Service is already running, return.
  2. Check whether the execution file of the Service to be started exists. If no, do not start the Service.
  3. If the Service is not started, the fork function is called to get the PID.
  4. If pid is 0, call the RunService method.

The RunService method enters the namespace, sets the environment variables, writes the PID file, and runs the service executable. It calls the ExpandArgsAndExecv function at the end. The execv function is finally executed in this method. Execv is a method defined in exec. CPP that calls the execve method internally.

The execve function executes the executable, starts the Service process, and enters the main function of the Service. If the Service is Zygote, app_main. CPP will be executed. The Zygote execution path is /system/bin/app/app_process64, and the corresponding file is app_main.cpp. This goes into the main function of app_main. CPP.

In the Zygote main function, the Runtime start function is called to start Zygote:

Zygote is the root process of the Android system, and the subsequent SystemSever and application processes are derived from Zygote fork. Therefore, Zygote child process can also be used as a Service. Call main to app_main. CPP. A flag is needed to distinguish Zygote from its child processes. Therefore, in the main function, if the parameter arg contains -zygote, it indicates that the zygote process is running. If no, the system checks -start-system-server. If yes, the system is running in the SystemServer process. The main method of app_main. CPP has the following code logic:

  1. Check the thread on which main is running.
  2. If running in Zygote, call Runtime.start. The runtime type is appruntime. CPP, and its start method does the following:
    1. Call startVm to start the JVM
    2. Register JNI methods for the JVM, (startReg(env))
    3. Learn from the main function of app_main classname for com. Android. Internal. OS. ZygoteInit
    4. Replace “. “with”/” in className
    5. Then find the ZygoteInit class based on the replacement path.
    6. Find the main method of ZygoteInit.
    7. JavaVM unable to find main() in $classname JavaVM unable to find main() in $classname
    8. If the main method is found, call ZygoteInit’s main method via JNI and call. We use JNI here because ZygoteInit’s main method was written in the Java language and is still Native. This takes Zygote from the Native layer into the Java framework layer.

ZygoteInit for the Java framework layer

The main function in the zygoteinit. Java file creates a Socket as the Server that waits for ActivityManagerService to request Zygote to create a new application process. The logic in main is as follows:

A. Create a Server Socket with socketName = zygote. B. Preload classes and resources. C. Start the SystemServer process. D. Wait for AMS request to create a new application process.Copy the code

Create Zygote Server Socket

zygoteServer.registerServerSocket(socketName);
Copy the code

ZygoteServer is of type zygoteServer, and its registerServerSocket method is used to create the Socket.

  1. Concatenate the socket name
  2. Get the socket’s environment variable
  3. Converts the value of the socket environment variable to the parameter of the file descriptor
  4. Create file descriptor fd
  5. Create Socket according to file descriptor fd

Start the SystemServer process

After the socket is created, the SystemServer process is started first. Starting SystemServer is the startSystemServer method in ZygoteInit. First, build an arGS array to store the startup parameters that start SystemServer. A child process, the SystemServer process, is then created using the Zygote. ForkSystemServer method. Finally, determine the current in the process of code, if it is to eat, call handleSystemServerProcess method processing SystemServer process.

Enable Socket listening

After starting the SystemServer process, ZygoteServer’s runSelectLoop method is executed. In this method, an infinite loop is created to wait for AMS requests.

Remember the file descriptor fd we created when we created the socket in the registerServerSocket method? Its data structure is:

Struct pollfd{int fd; // File descriptor short event; // The requested event is short revent; // Return event}Copy the code

In this endless loop to receive AMS requests, the file descriptor information is first fetched from the socket and stored in an array, represented by FDS. Then an infinite loop waits for AMS requests, each loop iterates over FDS, dumps the FD into pollFds, and calls the system’s poll method:

Os.poll(pollFDs, pollTimeoutMs);
Copy the code

This method is the last to call the poll function in Linux, which takes an array as its first argument. The poll function can monitor multiple file descriptors. You can learn about the poll function by following the link: man7.org/linux/man-p… . This function returns a value that will be zero if time out or if there is no FD to process, thus blocking and waiting for the message. If the result is not 0, the pollFds is traversed in reverse. If it reaches 0, the server Socket is connected to the client, but there are no FDS to process. The current Zygote process establishes a connection with AMS, and then obtains a ZygoteConnection object and adds it to the Socket’s connection list peer. ZygoteConnection’s FD is then added to the FDS so that it can accept requests from AMS. If the traversal is not zero, then AMS sent a request to the Zygote process to create an application process. ZygoteConnection’s runOnce function is called to create a new application process and, upon successful creation, the connection is removed from the Socket connection list peers and FD list FDS.

The reason for traversing to 0 here is that AMS must establish a connection with the Socket first, and then can receive only one AMS request, upon which the connection will be closed.

To summarize the flow of the runSelectLoop method:

  1. An infinite loop is created
  2. Fetch the file descriptor information of the Socket and store it in the array FDS.
  3. Iterate over FDS to save the data in FDS to pollFds.
  4. Call Linux’s poll method to check whether the file descriptor is to be processed.
  5. The value returned by poll determines whether there is a FD to process. If there is no FD, the next loop is started to achieve the blocking effect.
  6. The pollFds should be traversed in reverse:
    1. If 0 is traversed, the Socket establishes a connection with the client for the first time. The connection object ZygoteConnection is obtained and stored in peers, the Socket’s connection list. Add ZygoteConnection’s FD to the FDS to receive the request later.
    2. If it is processed before it reaches 0, AMS sends a request to Zygote to create an application process, calls the runOnce function area to create a new application process, and deletes the fd in FDS and the link in peers when the creation is complete.

conclusion

The Zygote process starts as follows:

  1. Create AppRuntime and call the start method to start the Zygote process.
  2. Create the JVM and register JNI methods for the JVM.
  3. The main function of ZygoteInit is called through the JNI method to enter the Java framework layer of Zygote from the Native layer.
  4. The server Socket is created through the registerZygoteSocket method, and the runSelectLoop method is used to wait for AMS to send a request to create an application process.
  5. Start the SystemServer process.