Zygote is a very important process in Android, it and Init process, SystemServer process in Android has an irreplaceable position.
Zygote
Introduction to the
Linux processes are generated by system call fork. The fork child processes share memory images with the parent process except some core data structures in the kernel. The operating system assigns a new page to the child only when it needs to overwrite the shared memory. This is called Copy On Write
Typically, when a child process is forked, the system call exec continues. Exec replaces the code segment, data segment, heap, and stack of the current process with the contents of a new executable file.
Fork and exec is standard Linux practice for starting applications, as is the Init process for starting various services.
However, Zygote created the application using only fork, without calling exec
First of all, Android applications run on virtual machines, and it is the Java code in virtual machines that makes the difference between applications, but the basic operating environment is the same.
Second, Zygote creates a virtual machine during initialization, loading the required system libraries and resource files into memory. When Zygote forks a child, the child gets a VIRTUAL machine that has already loaded the basic resources.
In this way, the next step is to load apK-related resources to run, which can improve efficiency
Zygote
Initialization of the process
Zygote is started as a service in Init. Let’s take a look at its configuration in init.rc:
import /init.${ro.zygote}.rc on late-init ...... Zygote trigger Zygote-start on zygote-start && # Now we can start zygote for devices with file based encryption # Start netd start zygote start zygote_secondaryCopy the code
Starting with Android 5.0, Android will support 64-bit compilation, and Zygote itself will have 32-bit and 64-bit differences. Therefore, the ro.zygote attribute controls the startup of different versions of the Zygote process. In the 9.0 source code, there are four related files:
init.zygote32.rc
init.zygote32_64.rc
init.zygote64.rc
init.zygote64_32.rc
Copy the code
Pick a particular one (init.zygote32_64.rc) :
service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main
......
service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
class main
......
Copy the code
- You can see from the file contents that two are defined
Zygote
Services:zygote
andzygote_secondary
- The biggest difference between the two services is
Executable file
Different: One isapp_process32
, and the other aapp_process64
init.zygote64_32.rc
I won’t post the file, but it’s just twoExecutable file
Discuss the- for
init.zygote32.rc
andinit.zygote64.rc
There’s only one of themZygote
service
From here we can see that Android will support four operating modes:
- Pure 32-bit mode:
ro.zygote
The value ofzygote32
- 32-bit mode is dominant, 64-bit mode is secondary:
ro.zygote
The value ofzygote32_64
- Pure 64-bit mode:
ro.zygote
The value ofzygote64
- 64-bit mode is dominant, 32-bit mode is secondary:
ro.zygote
The value ofzygote64_32
For the executable app_process, the source path is in frameworks/base/ CMDS /app_process, but let’s take a look at the app_process usage before we look at the source
app_process
The use of the
We know that Zygote is started by app_process
App_process parses startup parameters and selects different startup modes based on the parameters
To better understand app_process, let’s look at how it is used. The app_process parameter is described as follows:
fprintf(stderr,
"Usage: app_process [java-options] cmd-dir start-class-name [options]\n");
Copy the code
A little too neat…
Let’s take a look at some examples, using Zygote as an example:
/system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
Copy the code
Here’s an interesting version of the following directive: Run an Application containing the main function class named Test:
app_process -Djava.class.path=/data/local/tmp/Test.dex /data/lee Test
Copy the code
Combined with the source Usage(pure version)… And these two examples, we can sum them up like this:
-Xzygote
and-Djava.class.p**
: belong to[java-options]
, these parameters are passed toThe virtual machine
, and the parameter must be-
At the beginning, once you encounter the non-
or--
Said,[java-options]
The end of the/system/bin
and/data/lee
: belong tocmd-dir
, the program run directory, optionally specified, file operation will be the current path, most of the serious run in/system/bin
Under theTest
: belong tostart-class-name
Declare the name of the entry class, plus the package name if there is one--zygote
: belong to[options]
, these parameters are signed--
At the beginning. parameter--zygote
To startZygote
process
app_process
themain()
With the use of app_process in mind above, it will be easier to analyze the main function, as usual, looking at the whole:
int main(int argc, char* const argv[])
{
1. Create an AppRuntime object
// 2. Save the java-option parameter
// 3. Parse the Option argument
// 4. Prepare ZygoteInit or RuntimeInit parameters according to the result of parameter parsing
// 5. Change the process name to the string specified by nice-name
// 6. Start the Corresponding Java class based on the parsed result
}
Copy the code
Very clear ah, we look at the specific source code
createAppRuntime
object
The main () the source code:
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
Copy the code
AppRuntime
Is in theapp_process
Class defined in theAndroidRuntime
Class.AndroidRuntime
Class is a very important class at the bottom. It is mainly used to create and initialize virtual machines- The whole
main()
The rest of the process goes through thisruntime
To operate the
saveJava-Option
parameter
.for (i = 0; i < argc; i++) {
// omit the know_command correlation.if (argv[i][0] != The '-') {
// If it does not start with -, parsing ends
// Follow the app_process argument rules: java-option is followed by cmd-dir instead of starting with -
// Therefore, normal parsing to Java-option will stop
break;
}
if (argv[i][1] = =The '-' && argv[i][2] = =0) {
// If it is -- and followed by a null character, parsing ends
// I don't know what this is for
++i; // Skip --.
break;
}
runtime.addOption(strdup(argv[i]));
// Omit the printed information. }...Copy the code
parsingOption
parameter
The Option argument is called the Runtime argument in the source code
// All the parameters that need to be parsed are here
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;
Parent dir = parent dir = parent dir = parent dir = parent dir = parent dir
// No extra processing is required for CMD dir, just skip it
++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) {
// If it does not start with --, it is class name
className.setTo(arg);
break;
} else {
--i;
break; }}Copy the code
To prepareZygoteInit
orRuntimeInit
Required parameters
// Define a collection of character arguments
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.
// Not Zygote mode, not deleted official comment, happy not
// 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);
// Omit the print part. }else {
// We're in zygote mode.
maybeCreateDalvikCache();
if (startSystemServer) {
args.add(String8("start-system-server"));
}
// Omit the fetch of ABI attribute values.String8 abiFlag("--abi-list=");
abiFlag.append(prop);
args.add(abiFlag);
// In zygote mode, pass all remaining arguments to the zygote
// main() method.
// In Zygote mode, the parameters of app_process are uniformly packed into the parameter set
for(; i < argc; ++i) { args.add(String8(argv[i])); }}Copy the code
Change the process name tonice-name
Specified string
if(! niceName.isEmpty()) {// This is a function provided by AndroidRuntime to set the process name
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
/ / in AndroidRuntime. CPP
void AndroidRuntime::setArgv0(const char* argv0, bool setProcName) {
if (setProcName) {
int len = strlen(argv0);
if (len < 15) {
pthread_setname_np(pthread_self(), argv0);
} else {
pthread_setname_np(pthread_self(), argv0 + len - 15); }}memset(mArgBlockStart, 0, mArgBlockLength);
strlcpy(mArgBlockStart, argv0, mArgBlockLength);
}
Copy the code
Start the correspondingJava
class
if (zygote) {
// If it is --zygote, execute the ZygoteInit class to start the zygote process
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
// If class name is specified, the class passed in is executed through RuntimeInit
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
app_process
inAndroid
The application of
In addition to starting the Zygote process, app_process can also be used to execute a system’s Java classes
A good example is the common Android tool AM:
Am is a tool for sending intents, such as AM Start and AM broadcast.
But AM is really just a script file with a few lines of code, all of which is done by calling app_process
#! /system/bin/shif [ "$1"! ="instrument"]; then cmd activity"$@"
else
base=/system
export CLASSPATH=$base/framework/am.jar
exec app_process $base/bin com.android.commands.am.Am "$@"
fi
Copy the code
No, no, Android is a surprise, ha ha ha!
We can carefully read the Am. Java class, very characteristic, call relationship is really drop trouble god, refuel yo!
Start a VMAndroidRuntime
class
When analyzing main() of app_process, we know that AppRuntime inherits the AndroidRuntime class.
AndroidRuntime
Class is an important class that is responsible for startingThe virtual machine
As well asJava thread
AndroidRuntime
Class has only one instance object in a process, stored in a global variablegCurRuntime
In the
The constructor
AndroidRuntime class constructors are as follows:
AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :
mExitWithoutCleanup(false),
mArgBlockStart(argBlockStart),
mArgBlockLength(argBlockLength)
{
// Initialize the SKia graphics system
SkGraphics::Init();
// Pre-allocate enough space to hold a fair number of options.
mOptions.setCapacity(20);
// Can only be initialized once
assert(gCurRuntime == NULL); // one per process
gCurRuntime = this;
}
Copy the code
Starting a VM
This chapter is only a brief introduction to the virtual machine startup process. The implementation details will be included in a separate chapter
In the main() function of app_process, runtime.start() is finally called to execute the Java class. The start() function is defined in AndroidRuntime. The code is quite long, so let’s look at the overall flow first:
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
// 1. Print the startup log
// 2. Obtain system directory
// 3. Start the VM
// 4. Call onVmCreated
// 5. Register system JNI functions
// 6. Prepare parameters for the Main function of the Java class
// 7. Call the main method of ZygoteInit
}
Copy the code
The print startLog
Source code snippet:
ALOGD(">>>>>> START %s uid %d <<<<<<\n", className ! =NULL ? className : "(unknown)", getuid());
Copy the code
Let’s take a look at the Log fragment of the system boot process:
00:00:21. 226808 3244 3244 D AndroidRuntime: > > > > > > START com. Android. Internal. OS. ZygoteInit uid 0 < < < < < <Copy the code
- This period of
Log
Mark theAndroid
System startup - Because the next application process is from
Zygote
processfork
It’s coming out, so it’s not going to be executedstart()
The function - if
Android
The system oflog
Repeatedly appear in this section of content, while outputID
Are allZygote
, it indicates that the system may be faulty.Zygote
The process is constantly being restarted
Obtaining system directory
The source code snippet
const char* rootDir = getenv("ANDROID_ROOT");
if (rootDir == NULL) {
rootDir = "/system";
if(! hasDir("/system")) {
LOG_FATAL("No root directory specified, and /android does not exist.");
return;
}
setenv("ANDROID_ROOT", rootDir, 1);
}
Copy the code
- System directory from environment variables
ANDROID_ROOT
read - If it is not set, the default value is
/system
- The system directory is in
Init
Created in the process
- The system directory is in
- if
/system
The directory does not exist. Exit directly
Starting a VM
The source code snippet
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if(startVm(&mJavaVM, &env, zygote) ! =0) {
return;
}
Copy the code
Start the VIRTUAL machine using startVm, which we’ll look at later.
callonVmCreated
function
onVmCreated(env);
Copy the code
The onVmCreated function is a virtual function that actually calls an overloaded function of AppRuntime for app_process. Here is the code logic in AppRuntime:
virtual void onVmCreated(JNIEnv* env)
{
if (mClassName.isEmpty()) {
return; // Zygote. Nothing to do here.
}
// Omit some interesting comments related to class loading.char* slashClassName = toSlashClassName(mClassName.string());
mClass = env->FindClass(slashClassName);
if (mClass == NULL) {
ALOGE("ERROR: could not find class '%s'\n", mClassName.string());
}
free(slashClassName);
mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));
}
Copy the code
In AppRuntime’s onVmCreated function:
- If it is
Zygote
Process, variablemClassName
The value of the willnull
Will return immediately - If it’s an ordinary
Java classes
The call,mClassName
Will hold the name of the class toSlashClassName(mClassName.string())
The role of theThe name of the class
Converted to classFully qualified name
The name of the class
likecom.android.Zygote
intoFully qualified name
becomes/com/android/Zygote
Register the system’s JNI functions
The source code snippet
/* * Register android functions. */
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
Copy the code
StartReg registers JNI native functions in the global array gRegJNI with the virtual machine by calling register_jni_procs:
/* * Register android native functions with the VM. */
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{...// This function modifies the gCreateThreadFn function pointer to javaCreateThreadEtc()
// After that, the new child thread will
JavaCreateThreadEtc () -> javaThreadShell () -> AttachThread() -> JVM ->AttachCurrentThread()
// The normal C layer thread becomes the JVM threadandroidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc); .if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return - 1; }... }Copy the code
For the gRegJNI array, the format is
static const RegJNIRec gRegJNI[] = {
......
REG_JNI(register_android_util_Log),
......
REG_JNI(register_android_os_Binder),
......
REG_JNI(register_android_graphics_Paint),
......
REG_JNI(register_android_app_Activity),
......
};
Copy the code
Many elements are omitted, and each member of the array represents a JNI mapping of a class of files, where REG_JNI is a macro definition that calls the corresponding JNI registration method
Prepare parameters for the Main function of the Java class
The source code snippet
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);
}
Copy the code
The above is a very classic operation to create Java layer objects in Native layer:
- To create a
java.lang.String
Array object of - The array object is assigned element by element based on the parameters passed in
Call the main method of the ZygoteInit class
The source code snippet
// Convert to fully qualified name
char* slashClassName = toSlashClassName(className ! =NULL ? className : "");
// Get the class object of the class
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 {
// Execute main
env->CallStaticVoidMethod(startClass, startMeth, strArray);
}
// The main function is finished
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");
Copy the code
Before calling main:
- Through the first
GetStaticMethodID
To obtainmain
methodsID
- Then use the
CallStaticVoidMethod
To invoke theJava
Layer of the function
At this point, the Zygote process initialization moves to the Java layer, remember the startup logic in the main() function of app_process?
To recap:
if (zygote) {
// If it is --zygote, execute the ZygoteInit class to start the zygote process
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
// If class name is specified, the class passed in is executed through RuntimeInit
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
}
Copy the code
Next, let’s look at the Zygoteinit.java class
Initialization work –ZygoteInit
class
The ZygoteInit class is responsible for initializing the Java layer of the Zygote process. The entry method main() has the following code (more detailed comments) :
public static void main(String argv[]) {
// Create Zygote service management class to register socket listener
ZygoteServer zygoteServer = new ZygoteServer();
// Mark zygote start. This ensures that thread creation will throw
// an error.
// After this method is called, the virtual machine rejects the creation of the thread, which will generate an error
ZygoteHooks.startZygoteNoThreadCreation();
// Zygote goes into its own process group.
Os.setpgid(0.0);
/ /...
final Runnable caller;
try{... Omit print correlation// Start the DDMS VM monitoring debugging service
RuntimeInit.enableDdms();
// Parameter parsing
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])) {
// Start system services
startSystemServer = true;
} else if ("--enable-lazy-preload".equals(argv[i])) {
// Start lazy loading
enableLazyPreload = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
// Abi type, one CPU corresponds to one ABI
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
// Parse the socket name
socketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
// Unknown parameters throw an exception
throw new RuntimeException("Unknown command line argument: "+ argv[i]); }}// An exception will be thrown if ABI parameters are not specified
if (abiList == null) {
throw new RuntimeException("No ABI list supplied.");
}
// Register Zygote's socket listener port to receive messages for starting applications
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) {// Lazy loading is not enabled. Omit some log prints// Perform preloading operations, including system preloading classes, Framework resources, and openGL resourcespreload(bootTimingsTraceLog); . Omit some log print}else {
// The priority of the reset thread in case of lazy loading
Zygote.resetNicePriority();
}
// Do an initial gc to clean up after startup. Omit log print// Official note: Run several specified GC's, try to clean up generations of soft references and reachable objects, among other garbage
// This method works only before fork()gcAndFinalize(); . Omit log print// Some security-related initialization operations
Zygote.nativeSecurityInit();
// Zygote process unmounts root storage spaces.
// The purpose is to unmount the entire storage directory /storage and mount the temporary directory instead
// This action is related to Android's sandbox (isolated storage)
Zygote.nativeUnmountStorageOnInit();
/ / echo the front startZygoteNoThreadCreation () method
// Inform the virtual machine that threads can now be created in the Zygote process
ZygoteHooks.stopZygoteNoThreadCreation();
if (startSystemServer) {
// fork SystemServer
Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
// {@code r == null} in the parent (zygote) process, and {@code r ! = null} in the
// child (system_server) process.
// If r is empty, the zygote process is running
if(r ! =null) {
// if r is not empty, it is the incubated child process SystemServer
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.
// This part is executed in zygote process
// Enter an infinite loop to process the data received by the Zygote socket
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
} finally {
// Note that this section is mainly for child processes
// There is no need to have zygote service in the child process
// So the shutdown is theoretically to shut down the useless Zygote service in the child process
zygoteServer.closeServerSocket();
}
// We're in the child process and have exited the select loop. Proceed to execute the
// command.
// All processes except systemServer actually start the main function here
RunSelectLoop is a wireless loop. How can I get around it?
// Why does it have to be a child process?
if(caller ! =null) { caller.run(); }}Copy the code
After reading the main function, you must have a lot of questions, because of the child process, the code processing logic is a bit complicated
Now I will learn the process of doubt and understanding to share with you
Knowledge:fork
The results of the
For fork, we need to remember:
fork
A new process is created that is identical to the current process- in
fork
After the function call- A new process will start
- And with the current process from
fork
The function returns
- about
fork
The return value of the- New process returns
0
- The current process returns the value of the new process
pid
- New process returns
Question 2:systemserver
The launch of the
if (startSystemServer) {
// fork SystemServer
Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
// {@code r == null} in the parent (zygote) process, and {@code r ! = null} in the
// child (system_server) process.
// If r is empty, the zygote process is running
if(r ! =null) {
// if r is not empty, it is the incubated child process SystemServer
r.run();
return; }}Copy the code
ForkSystemServer this code implements forkSystemServer forking with forkSystemServer logic:
-
Run pid = zygote. forkSystemServer
- Note that two processes are generated after execution:
zygote
andsystemserver
- Also, both processes start execution at the same time, but the process receives
pid
It’s different - Refer to the previous one
knowledge
- Note that two processes are generated after execution:
-
Pid judgment:
// For systemServer processes, pid is 0 if (pid == 0) { if (hasSecondZygote(abiList)) { waitForSecondaryZygote(socketName); } zygoteServer.closeServerSocket(); / / handleSystemServerProcess this function will be: // Find SystemServer's main function by reflection // wrap it in Runnable's run function return handleSystemServerProcess(parsedArgs); } // pid does not equal 0. The parent process, zygote, returns null return null; Copy the code
At this point, you should understand the following code processing meaning:
if(r ! =null) {
r.run();
return;
}
Copy the code
Question 3:main
Last of the functioncaller.run()
How does it work
The application should be launched through this caller.run(), and there’s no problem figuring it out
public static void main(String argv[]) {...try{... caller = zygoteServer.runSelectLoop(abiList); }finally{ zygoteServer.closeServerSocket(); }...if(caller ! =null) { caller.run(); }}Copy the code
The official comment already says caller.run(); Is the child really run, but zygoteServer. RunSelectLoop not a wireless loop, zha of?
Let’s look at the runSelectLoop function:
Runnable runSelectLoop(String abiList) {...while (true) {...try {
ZygoteConnection connection = peers.get(i);
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.. }}... }Copy the code
Two important points:
- a
mIsForkChild
attribute- when
mIsForkChild
Properties fortrue
, will exit the loop - And return a
Runnable
object
- when
- a
processOneCommand
function
For the processOneCommand function, it also has an important operation:
Runnable processOneCommand(ZygoteServer zygoteServer) {...// fork the child processpid = Zygote.forkAndSpecialize(...... ;if (pid == 0) {
// in child
// Set mIsForkChild to truezygoteServer.setForkChild(); zygoteServer.closeServerSocket(); .// handleChildProc This function will:
// Use reflection to find the main function that starts the class
// wrap it in Runnable's run function
returnhandleChildProc(......) ; }else{...return null; }}Copy the code
In Zygote’s infinite loop, when listening for data:
- through
processOneCommand
functionfork
Out of theThe child process
At this time:- Two processes are running:
zygote
andThe child process
- Two processes are running:
- Still based on
pid
To determine which one isThe child process
- for
The child process
To set upmIsForkChild
Properties fortrue
- and
The child process
Pack upRunnable
The object returned - for
zygote
To return tonull
- for
The runSelectLoop() in the child stops the loop and executes to caller.run().
The Android 5.0 source code executes caller.run() by throwing an exception
With that in mind, it should be easy for zygote to start the application
Zygote
Start the application
We already know that Zygote performs the task of starting the application by using runSelectLoop() as a daemon. Take a look at the sequence diagram of the process and explain each step in detail
Register the Zygote socket
The ZygoteInit class’s main() function
- First call
ZygoteServer
theregisterServerSocketFromEnv
To create a localsocket
- Then call
runSelectLoop
Come in and waitsocket
Connected loops
RegisterServerSocketFromEnv () function is as follows:
void registerServerSocketFromEnv(String socketName) {
if (mServerSocket == null) {
int fileDesc;
// The fullSocketName is ANDROID_SOCKET_zygote
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
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);
mCloseSocketFd = true;
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex); }}}Copy the code
The ANDROID_SOCKET_zygote function retrieves fileDesc of the socket using the environment variable ANDROID_SOCKET_zygote.
- this
FileDesc (File descriptor)
How did it come about? So let’s go back a little bitinit.rc
Contents of:service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote ...... socket zygote stream 660 root system ......Copy the code
Zygote
One is specified when the service startsoption
Used forsocket
The creation ofInit
The process uses this option to create one for local communicationsocket
- And to bring the
socket
thefileDesc
Put it in an environment variableANDROID_SOCKET_zygote
In the - The following part of the environment variable string
zygote
isoption
The name of the
- get
fileDesc
Later, byLocalServerSocket()
Create a localsocket
Service and save to a global variablemServerSocket
In the
Requesting to start the application
Android starts a new process in ActivityManagerService. There are many reasons to start a new process in ActivityManagerService by calling startProcess() :
final String entryPoint = "android.app.ActivityThread";
private ProcessStartResult startProcess(String hostingType, String entryPoint,
ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
String seInfo, String requiredAbi, String instructionSet, String invokeWith,
long startTime) {...final ProcessStartResult startResult;
if (hostingType.equals("webview_service")) {... }else {
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith,
newString[] {PROC_START_SEQ_IDENT + app.startSeq}); }... }Copy the code
As you can see, the key function is process.start (). And Process. The start () the first parameter to the entryPoint is to start after the execution of Java classes: android. App. ActivityThread
What parameters are sent to Zygote when the application is launched? The application startup process will be expanded later
Let’s trace process.start ():
// class:Process
public static final ProcessStartResult start(final String processClass ......) {
returnzygoteProcess.start(processClass......) ; }// class:ZygoteProcess
public final Process.ProcessStartResult start(final String processClass ......) {
returnstartViaZygote(processClass ......) ; }private Process.ProcessStartResult startViaZygote(final String processClass ......){
ArrayList<String> argsForZygote = new ArrayList<String>();
// --runtime-args, --setuid=, --setgid=,
// and --setgroups= must go first
argsForZygote.add("--runtime-args"); . argsForZygote.add(processClass);return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
Copy the code
A little bit too many parameters, code part meaning ha, heh heh
start()
The method callstartViaZygote()
Method to start the applicationstartViaZygote()
Save the startup parameters of the application process toargsForZygote
In the collection- And then call
zygoteSendArgsAndGetResult
Method to send the startup parameters of the application processZygote
process zygoteSendArgsAndGetResult()
Method is calledopenZygoteSocketIfNeeded()
Method is used to create asocket
, the code looks like this:public static final String ZYGOTE_SOCKET = "zygote"; public static final LocalSocketAddress address = new LocalSocketAddress(ZYGOTE_SOCKET, LocalSocketAddress.Namespace.RESERVED); final LocalSocket zygoteSocket = new LocalSocket(); zygoteSocket.connect(address); Copy the code
- for
Local Socket
Using a string as an address can communicate, very convenient
- for
socket
Once the connection is established,zygoteSendArgsAndGetResult
Take advantage of thissocket
Write startup parameters:writer.write(Integer.toString(args.size())); writer.newLine(); for (int i = 0; i < sz; i++) { String arg = args.get(i); writer.write(arg); writer.newLine(); } writer.flush(); Copy the code
At this point, the parameters requesting application startup are sent to the Zygote process. Let’s take a look at the Zygote process
Process requests to start applications
The Zygote process uses runSelectLoop() to listen for and process requests to start applications.
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
// Add the socket zygote file descriptor to the FDS set
Note here that mServerSocket is the first element in the collection
fds.add(mServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
// Dynamically generate a pollFds array as a poll listener
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 {
// Event listening for files in the pollFds array via poll
// -1 blocks until data comes in
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
// This will be executed when data arrives
for (int i = pollFds.length - 1; i >= 0; --i) {
// Loop over to find the file that received the data
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
// if I =0, it is mServerSocket
// Indicates that the client initiated a connection request
// Following the LocalServerSocket process, a LocalSocket needs to be created for client communication
// acceptCommandPeer() creates the ZygoteConnection object according to this process
// The real communication object is LocalSocket
// Then add the FD of the communication object to the FDS collection
// Don't bother with this part of the processing method, as LocalSocket standard usage can be
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
try {
// This is the real processing part
ZygoteConnection connection = peers.get(i);
// Execute the processOneCommand function corresponding to connection
// The function reads the parsed parameters and performs operations related to the child process fork
final Runnable command = connection.processOneCommand(this);
if (mIsForkChild) {
......
// Exit the selectLoop loop
return command;
} else{...// the zygote process is closed to remove connection
if(connection.isClosedByPeer()) { connection.closeSocket(); peers.remove(i); fds.remove(i); }}}catch (Exception e) {
if(! mIsForkChild) {// Clear the exception. ZygoteConnection conn = peers.remove(i); conn.closeSocket(); fds.remove(i); }else{... }}finally{... mIsForkChild =false;
}
}
}
}
}
Copy the code
We know from the above code, real processing for news is in ZygoteConnection processOneCommand function, we have to focus on:
processOneCommand
function
The function is divided into three parts:
- Parsing startup parameters
- The child process
fork
- The child process is initialized
Let’s take a look at each of them
Parsing startup parameters
The flow of parameter resolution is
- Through the first
readArgumentList
Methods fromsocket
Multiple parameter rows are read from the connection- The style of the argument line is
--setuid=1
- Between the lines
\r
,\n
or\r\n
segmentation
- The style of the argument line is
- Call it after reading
Arguments
Of the classparseArgs
Method parses the data into a list of parameters- Specific parameter meaning, you can refer to
Arguments
Class notes
- Specific parameter meaning, you can refer to
After parsing the parameters, processOneCommand also checks and sets the parsed parameters:
. Omit some parameter attribute judgments// Check that the client has the permission to specify the user ID and group ID of the process
// If the process is root, you can specify any process
// If it is the sys process, this parameter can be specified when the value of ro.factorytest is > 0
applyUidSecurityPolicy(parsedArgs, peer);
// Determine whether invoke-with execution permission is available
applyInvokeWithSecurityPolicy(parsedArgs, peer);
// If ro.debuggable is 1, start JDWP protocol
applyDebuggerSystemProperty(parsedArgs);
// Handle the invoke-with attribute
applyInvokeWithSystemProperty(parsedArgs);
Copy the code
I won’t go into the details
fork
The child process
Once the parameters are checked, the processOneCommand function calls the ForkandWpecte method to fork the child process:
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
parsedArgs.instructionSet, parsedArgs.appDataDir);
Copy the code
ForkAndSpecialize finally is done through native layer ForkAndSpecializeCommon fork, we simply introduce the main work of the function:
- through
fork
Creating a child process - Mount in child process
emulate storage
- Set the user ID, group ID, and group to which the process belongs in the child process
- Passes in the child process
setrlimit
The system call sets the system resource limit for the process - Passes in the child process
capset
The system call sets the permissions of the process - Passes in the child process
selinux_android_setcontext
Set the security context for the application process
After returning to the Java layer from the Native layer
- if
pid==0
Is in a child processhandleChildProc
function - if
pid! = 0
Is in the Zygote processhandleParentProc
Function, and returnsnull
This part of the code has been shown in the previous question:
if (pid == 0) {
// in child
zygoteServer.setForkChild();
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
return handleChildProc(parsedArgs, descriptors, childPipeFd,parsedArgs.startChildZygote);
} else {
// In the parent. A pid < 0 indicates a failure and will be handled in
// handleParentProc.
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
handleParentProc(pid, descriptors, serverPipeFd);
return null;
}
Copy the code
Initialization of the child process
After the Zygote process forks the child process, it calls the handleChildProc method to initialize the child process.
handleChildProc
Method first turns off the listenersocket
And from theZygote
File descriptor inherited fromcloseSocket(); .for(FileDescriptor fd: descriptors) { IoUtils.closeQuietly(fd); }...Copy the code
- Next, depending on whether the startup parameter has
--runtime-init
As well as--invoke-with
How do I initialize itif(parsedArgs.invokeWith ! =null) { WrapperInit.execApplication(......) ; . }else{...returnZygoteInit.zygoteInit(......) ; . }Copy the code
- Start the
apk
The application will have--runtime-init
Parameter, but--invoke-with
Usually in the form ofnull
--invoke-with
Don’t fornull
Will be able to passexec
The way to startapp_process
To perform theJava
class- Normally this is called
ZygoteInit.zygoteInit
methods
- Start the
ZygoteInit.zygoteInit
The method calls three more methods:RuntimeInit.commonInit()
,ZygoteInit.nativeZygoteInit()
,RuntimeInit.applicationInit()
And the lastreturn
aRunnable
Object to the callercommonInit()
Simple initialization of some common configurations:- Set up the
KillApplicationHandler
For the defaultUncaughtExceptionHandler
- Set the time zone
- Set up the
http.agent
Property forHttpURLConnection
- reset
Android
theLog
system - through
NetworkManagementSocketTagger
Set up thesocket
thetag
Is used to collect traffic statistics
- Set up the
nativeZygoteInit()
- This is a local method, but it’s important because it does one thing
static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz) { gCurRuntime->onZygoteInit(); } Copy the code
gCurRuntime
? Does it look familiar? It isAppRuntime
And you can go up and look at itAndroidRuntime
The initialization part- And then called
onZygoteInit
Let’s seeAppRuntime
Function implementation:
virtual void onZygoteInit(a) { sp<ProcessState> proc = ProcessState::self(); proc->startThreadPool(); } Copy the code
- This code does not also look familiar, ha ha ha, in
Binder
We’ve already studied the chapters - Mainly initialization
Binder
So that the application process can use itBinder
the
applicationInit()
After the first two steps of initialization are complete, this function is executed and returned- Specifies the parameter for configuring the VM
HeapUtilization
for0.75 f
- Set the current
SDKVersion
- In addition to the above two, the most important is called
findStaticMain()
Function to findJava
Of the classmain
Method, and packaged intoRunnable
In the form of
protected static Runnable findStaticMain(...) { Class<? > cl = Class.forName(className,true, classLoader); Method m = cl.getMethod("main".new Class[] { String[].class }); int modifiers = m.getModifiers(); return new MethodAndArgsCaller(m, argv); } static class MethodAndArgsCaller implements Runnable { private final Method mMethod; private finalString[] mArgs; .public void run(a) {... mMethod.invoke(null.newObject[] { mArgs }); . }}Copy the code
- At this point, the function call ends
- These are all some
Java reflection
The relevant knowledge, not clear words to learn oh
- Specifies the parameter for configuring the VM
processOneCommand
End point of a function
After the processOneCommand function has been executed, two things happen:
- for
The child process
Return all packedMethodAndArgsCaller
The instance - for
Zygote
Continue the cycle
At this point, the child process’s runSelectLoop exits the loop and returns MethodAndArgsCaller, which eventually executes to:
caller = zygoteServer.runSelectLoop(abiList);
if(caller ! =null) {
caller.run();
}
Copy the code
Preloads system classes and resources
To speed up application startup, Android preloads common Java classes and some Framework resources into the Zygote process. These preloaded classes and resources are shared among all child processes forked by the Zygote process.
As shown in the figure:
The preload operation is done in the zygoteinit.main () function, via the preload() function, as follows:
static void preload(TimingsTraceLog bootTimingsTraceLog) {...// Set soft reference protection to avoid GC collectionbeginIcuCachePinning(); .// Load the system classpreloadClasses(); .// Load system resourcespreloadResources(); .// nativePreloadAppProcessHALs(); .// Load OpenGL resources
preloadOpenGL();
// Load some shared so libraries (android, compiler_rt, jnigraphics)
preloadSharedLibraries();
// Load font resourcespreloadTextResources(); .// Load webView-related resources
WebViewFactory.prepareWebViewInZygote();
// Disable soft reference protection
endIcuCachePinning();
// Initialize JCA security-related parameters
warmUpJcaProviders();
Log.d(TAG, "end preload");
sPreloadComplete = true;
}
Copy the code
The comments are more detailed, and we’ll focus on loading Java classes, system resources, and shared libraries
Preload Java classes
Android puts all Java classes that need to be preloaded into text files called preload-classes.
In 9.0 source code files in the repository paths is in frameworks/base/config/preloaded – classes, content format is as follows:
#
# This file has been derived for mainline phone (and tablet) usage.
#
......
android.animation.Animator
android.app.ActivityThread
android.app.FragmentManager
......
Copy the code
The preloadClasses() function parses this file and loads all classes declared in it.
private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
private static void preloadClasses(a) {... InputStream is =newFileInputStream(PRELOADED_CLASSES); . BufferedReader br =new BufferedReader(new InputStreamReader(is), 256);
int count = 0;
String line;
while((line = br.readLine()) ! =null) {
line = line.trim();
if (line.startsWith("#") || line.equals("")) {
continue; }... Class.forName(line,true.null); . }}Copy the code
At this point, the virtual machine is just finished loading these classes. The class is actually initialized only when it is first used actively
preloaded-classes
With all these classes declared, how is it generated?
The generation process is:
- First of all, there’s one
frameworks/base/tools/preload/WritePreloadedClassFile.java
Class, which corresponds tomodule
ispreload
- And then there’s another one
frameworks/base/tools/preload/20100223.compiled
file - Then through
mmm frameworks/base/tools/preload
You can compilepreload.jar
- At last,
java -Xss4M -cp out/host/linux-x86/framework/preload.jar WritePreloadedClassFile frameworks/base/tools/preload/20100223.compiled
And the result of that parsing is one that contains everythingJava classes
The contents of the list include:- The root object
- Loaded by several processes
- Average loading time
- The last file generation path is in
/frameworks/base/preloaded-classes
Compiled file compiled compiled is compiled without an compiled compiled file.
- First of all, there’s one
frameworks/base/tools/preload/Compile.java
Class, also inpreload
In, it is used for analysislog
thepreload
The information of - And then we can go through
java -Xss512M -cp out/host/linux-x86/framework/preload.jar Compile logcat.txt 20201111.compiled
To generate the analysis, which is.compiled
file
Preloading system resources
The function for preloading system resources is preloadResources().
private static void preloadResources(a) {... mResources = Resources.getSystem(); mResources.startPreloading();if (PRELOAD_RESOURCES) {
......
TypedArray ar = mResources.obtainTypedArray(com.android.internal.R.array.preloaded_drawables);
intN = preloadDrawables(ar); ar.recycle(); . ar = mResources.obtainTypedArray(com.android.internal.R.array.preloaded_color_state_lists); N = preloadColorStateLists(ar); ar.recycle(); . } mResources.finishPreloading(); . }Copy the code
The two functions for preloading the Framework’s resource core are:
preloadDrawables
Loading:drawable
resourcespreloadColorStateLists
Loading:color
State definition resource
Behind a specific resource loading process to study it in detail, we first look at the definition of the preload resources, in the source directory frameworks/base/core/res/res/values/arrays. The XML:
<array name="preloaded_drawables">
<item>@drawable/action_bar_item_background_material</item>
<item>@drawable/activated_background_material</item>
</array>
<array name="preloaded_color_state_lists">
<item>@color/primary_text_dark</item>
<item>@color/primary_text_dark_disable_only</item>
</array>
Copy the code
Preload the shared library
The function for preloading shared libraries is preloadSharedLibraries() :
private static void preloadSharedLibraries(a) {
Log.i(TAG, "Preloading shared libraries...");
System.loadLibrary("android");
System.loadLibrary("compiler_rt");
System.loadLibrary("jnigraphics");
}
Copy the code
There are three SO files in total:
libandroid.so
libcompiler_rt.so
libjnigraphics.so
conclusion
Zygote process learning is over here, and further deepen the understanding of Android, ha ha ha ha!
Why is there a conclusion here at 叒叒叒叒? Ha ha ha ha, curious baby can look here!
A private project of no consequence
Knowledge is knowledge after all, skills still need to hone drops! Next article start learning Android resource management, come on ~~