Previous
- User mode 1 (PID =1) application init initiates zygote startup via app_process
- App_process initializes and starts the JVM by operating on AppRuntime (a derivative of AndroidRuntime)
- The ART virtual machine is started, the JNI call environment is initialized, and many Android API-related JNI are registered
- Env ->CallStaticVoidMethod() initiate a call to ** zygoteinit.java #main()** and the world switches to Java
The total level calls are:
- bootloader
- kernel
- Init (switch to user mode for the first time)
- App_process64 (for 64-bit models) starts the Java world here, starting Zygote
- Init (switch to user mode for the first time)
- kernel
What does zygoteinit.java’s main() do?
Source location: frameworks/base/core/Java/com/android/internal/OS/ZygoteInit. Java
New ZygoteServer, ZygoteHooks, setpgid
-
Create ZygoteServer, which exposes the API using sockets (more on ZygoteServer later)
-
ZygoteHooks then interact with the virtual machine using ZygoteHooks, which tell the virtual machine not to start new threads during Zygote creation or an error will be reported
-
Os.setpgid(0,0) does not really set its PID to 0, see the official explanation for the underlying method
Extraneous parts are hidden. The Linux API manual states that if both are given 0, the pid and pGID of the current process will be followed (in zygote’s case, the pid and pGID of the process generated when init calls app_process).
Can make the DDMS
-
RuntimeInit.enableDdms()
-
android.ddm.DdmRegister.registerHandlers()
-
The methods we use in our App to debug and view threads, memory, UI layout, and so on are registered here.
If you’re doing a performance collection project, learn how the system works and start here.
Analytical parameters
Remember the arguments passed to the ZygoteInit#main() method?
- com.android.internal.os.ZygoteInit
- start-system-server
- –abi-list=arm64-v8a
So it makes sense that I starts at 1 in the for loop. And where did sockets come from? And this one, you can go back and look at what init does when it starts a service.
Zygote socket episode
Recall the init. Zygote64. Rc
When doing rc file parsing, Service parsing process:
system/core/init/service.cpp
- Service::ParseLine(text content for each line)
- OptionParserMap: : FindFunction (passthrough) / / find ParseSocket through socket function pointer
- Construct SocketInfo to Descriptors_
- OptionParserMap: : FindFunction (passthrough) / / find ParseSocket through socket function pointer
When Service:Start() is called:
- DecriptorInfo::CreateAndPublish()
- SocketInfo::Create()
- util.cpp#CreateSocket()
- Setenv // For SocketInfo the environment variable ANDROID_SOCKET_socket_name is set
- SocketInfo::Create()
A socket named zygote is created when the Service is started.
/dev/socket/zygote
After creation, the socket handle data is placed under the key of ANDROID_SOCKET_zygote in the form of environment variables.
Back to parameter parsing. By default, socketName = zygote is set to socketName = zygote. There is no socket configuration in this parameter, so zygote is the final valid socket name
The connection socket
Init (zygoteServer) ¶ zygoteServer (zygoteServer) ¶
LazyPreload lazy – preload
If enableLazyPreload is false, preload() will be performed. The logic here is:
- If it’s Zygote, which means lazy loading is not required, pre-load something
- Thread priority is lowered if lazy loading is required. Can be understood as, need lazy load, then inevitably represents is not very important
Preload contents:
For example, you can preload Java classes and Android internal classes, such as Activity, Fragment, Service, etc., using class.forname:
Take my AOSP source environment for example, which is huge, up to 6500+ lines:
The Zygote. NativeSecurityInit () access security
Next, call the Zygote. NativeSecurityInit (); Initialization of native, corresponding to: framworks/base/core/jni/com_android_internal_os_Zygote.cpp# com_android_internal_os_Zygote_nativeSecurityInit()
As a refresher, this is done during AndroidRuntime initialization, before entering the Java world, as detailed in the previous article
The specific content is to operate SELinux, because the system design, only the system can do SELinux related operations, App process is not allowed. As one of the biggest players in the Java world, Zygote naturally wants to do security before forking out other apps.
Isolated storage Zygote. NativeUnmountStorageOnInit ()
In this step calls the com_android_internal_os_Zygote. CPP in com_android_internal_os_Zygote_nativeUnmountStorageOnInit () method. The purpose is to unmount the entire /storage directory and mount the temporary directory instead. This action is related to the isolated storage mentioned in Android9 and 10, also known as sandbox storage. Please refer to the official instructions for this part.
ZygoteHooks.stopZygoteNoThreadCreation()
If the red line is raised, InternalError will be raised. If the red line is raised, InternalError will be raised. If the red line is raised, InternalError will be raised.
Before going any further, there is a key player ZygoteServer that needs to be clarified
ZygoteServer
It is in zygoteinit.java #main(), and in addition to the constructor, the methods to be called are as follows:
- registerServerSocketFromEnv(socketName) //zygote
- runSelectLoop()
- closeServerSocket()
registerServerSocketFromEnv
ANDROID_SOCKET_zygote => ANDROID_SOCKET_zygote => ANDROID_SOCKET_zygote => ANDROID_SOCKET_zygote
In this method, we get the handle data for the socket using the corresponding environment variable and convert it to ServerSocket in Java space:
In LocalServerSocket, real listening is started through LocalSocketImpl.
You just register, you start listening, but you don’t get the data yet
runSelectLoop
The ZygoteServer poll checks the Zygote socket. The two types of sockets are distinguished during the check:
- A new ZygoteConnection will be constructed and put into the next round of rotation handle set (ServerSocket position in the set is always 0), and a new round of rotation will be started
- For ZygoteConnection, that is, the connection from the client. Will be called
ZygoteConnection.processOneCommand()
The fork occurs when the command is parsed to get the corresponding Runnable executable. The fork closes the connection in the parent process and the Runnable is returned in the child process
The process is as shown in the figure above, ignoring the order of the sequence, because the child process is fork, so it depends on the order of the link
closeServerSocket
Finally, as the name implies, close the ServerSocket and no longer accept connections. In other words, the Zygote process is terminated, which is a fatal error for The Android system and is unlikely to happen under normal operating conditions. So why do we have this method?
In the sequence diagram above, there is a point where the parent process does not return after a fork, but closes the connection from the client in ZygoteConnection (because it has already been processed). The zygote service is not required in the child process of the fork, so the shutdown is in theory to shut down the child process of the useless Zygote service.
conclusion
After analyzing the three key methods of ZygoteServer, it is found that zygote services and clients are positioned as connectors and processors. The client sends some startup requests through the socket named Zygote, and the Zygote process forges the child process to enjoy everything zygote does in the initial startup stage (JVM initialization, JNI initialization, class preloading, resource preloading, etc.). Then execute the Runnable parsed by the command (the process of parsing is directly listed below). This is one of the most important parts of a new App launch process, and will be touched on in the following sections.
ProcessOneCommand Parses the Runnable process
- ZygoteConnection.processOneCommand
- ReadArgumentList // Reads the argument list from the socket
- New Arguments // Parse Arguments
- fork
- The parent process
- return null
- The child process
- ZygoteServer. SetForkChild () / / told ZygoteServer current in the child process
- ZygoteServer. CloseServerSocket () / / as mentioned, the child is no longer need a zygote
- handleChildProc
- SetArgV0 // Set the zygote Process Name to the same set
- ZygoteInit.zygoteInit()
- RuntimeInit.applicationInit()
- FindStaticMain () / / implementation of an android app. ActivityThread# main () method is Runnable
- RuntimeInit.applicationInit()
- The parent process
fork SystemServer
After ZygoteServer comb, now back to ZygoteHooks. StopZygoteNoThreadCreation systemserver after () in the process of the fork
Arguments
Here are the key parameters used to start system_server:
Uid, gid, and group information, niceName, runtime parameters, as well as the most important classPath = com. Android. Server SystemServer.
The Arguments above are resolved by ZygoteConnection’s internal class Arguments.
--
Is treated as a parameter character to skip- Other parameters will be repeated parsing error
When all the known content has been parsed, it checks to see if there are any arguments left:
In accordance with the above for launching system_server parameters, we can determine here, will go to the last case, namely the rest of the parameters will be stored in remainingArgs, namely com. Android. Server SystemServer
Zygote.forkSystemServer()
After the parameters are parsed, ZygoteInit calls Zygote.forkSystemServer() to start forking.
After this party runs the call, two returns are triggered, in uncertain (and equally unimportant) order.
- A return is a return with a PID greater than 0. This pid is the parent pid of the fork
- The other return is with the PID equal to 0, while runtime is in the child process
ZygoteHooks.preFork()
Here’s how to prepare for the fork, mainly by calling daemon.stop () to stop the Daemon thread:
- Heap guardian
- Reference queue daemon
- Memory reclamation daemon
- The memory reclamation watchdog daemon thread
resetNicePriority()
The reset process (main thread) has a priority of 5, which can be seen from our own App thread information
nativeForkSystemServer()
This is where a real fork takes place, causing the whole process to initiate two returns. A key detail:
After the fork completes, a Java space callback (zygote.java) is triggered in the system_server process (child) via JNI:
This call finally takes effect in the ART virtual machine, which does a series of configurations for the child processes derived from Zygote. Of course, there are many special treatments for system_server (such as JIT off) along the way.
ZygoteHooks.postForkCommon()
Here the four daemons mentioned above are started again and the thread priority is reset. Crucially, this is done separately in the parent and child processes. Zygote process, this is the first time to start these daemons. This is where the daemon thread for the system_server process, and possibly other tripartite apps, starts.
At this point, the system_server process forks and then starts working in the process
Run in the sysetm_server process
return null
The last return NULL is used to clarify the problem.
In the ZygoteInit#main() method, we analyzed the last paragraph earlier, to review:
After fork completes system_server, null is returned directly from the parent process. In this case, the Zygote process executes a runSelectLoop, which, as explained above, processes the commands received by the Zygote socket.
In the system_server child process
About second Zygote sockets
If there are more than one ABI (arm64, etc.), a second Zygote socket will be given priority to wait for the second socket to be ready. The method is very traditional, wait 20 seconds:
We won’t talk too much about this, but we’ll focus on the main thread.
zygoteServer.closeServerSocket()
ZygoteServer is disconnected because the child process does not need to keep the Zygote socket connected. This does not affect the server socket of the main process to continue working.
handleSystemServerProcess()
Above is the key point about the ginseng, there are one has brought, also in parsedArgs remained a variable called remainingArgs, its content is com. The android. Server SystemServer.
- First, by calling the familiar
Process.setArgV0(parsedArgs.niceName)
Rename the system_server process tosystem_server
- Then, the SYSTEMSERVERCLASSPATH environment variable is used to retrieve some JAR files (actually the jar file in /system/frameworks/ on the final machine, which is the classpath for system_server). Dex Opt optimization ahead of time (the installd service section is not covered here)
- Next, create a classloader (PathClassLoader) and set it to the main thread
- Finally, construct the executable Runnable with ZygoteInit (passing in the remainingArgs we care about here)
And finally, it’s this Runnable that gets returned in the ZygoteInit#main method that gets called. Finally call the com. Android. Server SystemServer# main () method, the specific content for the next analysis.