1 Introduction to Zygote
Zygote is the most important process in Android, Zygote进程和Init进程、SystemServer进程是Android最重要的三大进程
. Zygote is the core process for creating a new process in the Android system. It is responsible for starting the Dalvik virtual machine, loading some necessary system resources and system classes, starting the system_server process, and then waiting to process app application requests.
In the Android system, the application process is hatched by the Zygote process, and the Zygote process is started by the Init process. The Zygote process will create a Dalvik virtual machine instance when it starts, and whenever it hatches a new application process, it will copy the Dalvik virtual machine instance to the new application process, so that each application process is There is a standalone Dalvik virtual machine instance.
Classes involved in Zygote:
frameworks/base/cmds/app_process/app_main.cpp
frameworks/base/core/jni/AndroidRuntime.cpp
frameworks/base/core/java/com/android/internal/os/
- Zygote.java
- ZygoteInit.java
- ZygoteServer.java
- ZygoteConnection.java
2 Zygote start
This article is based on Android10(Q)
the source code analysis
2.1 The init process parses the init.rc script
Zygote is started by init进程
the parsing script. init.rc
The script is passed in app_process的main方法
for segmentation, and the corresponding logic is performed according to the string command.
Now the machine is divided into 32-bit and 64-bit, Zygote's startup script init.rc is also different:
- init.zygote32.rc: The execution program corresponding to the zygote process is app_process (pure 32bit mode)
- init.zygote64.rc: The execution program corresponding to the zygote process is app_process64 (pure 64bit mode)
- init.zygote32_64.rc: Start two zygote processes, the corresponding execution programs are app_process32 (main mode), app_process64
- init.zygote64_32.rc: Start two zygote processes, the corresponding execution programs are app_process64 (main mode), app_process32
The program to be executed by zygote is system/bin/app_process
, and its source code is here app_main.cpp
. Let's take a look at how app_main handles script commands:
frameworks/base/cmds/app_process/app_main.cpp
165 #if defined(__LP64__)
166 static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist64";
167 static const char ZYGOTE_NICE_NAME[] = "zygote64";
168 #else
169 static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist32";
170 static const char ZYGOTE_NICE_NAME[] = "zygote";
171 #endif
172
173 int main(int argc, char* const argv[])
174 {
......
256 // Parse runtime arguments. Stop at first unrecognized option.
257 bool zygote = false;
258 bool startSystemServer = false;
259 bool application = false;
260 String8 niceName;
261 String8 className;
262
263 ++i; // Skip unused "parent dir" argument.
264 while (i < argc) {
265 const char* arg = argv[i++];
266 if (strcmp(arg, "--zygote") == 0) {
267 zygote = true;
268 niceName = ZYGOTE_NICE_NAME;
269 } else if (strcmp(arg, "--start-system-server") == 0) {
270 startSystemServer = true;
271 } else if (strcmp(arg, "--application") == 0) {
272 application = true;
273 } else if (strncmp(arg, "--nice-name=", 12) == 0) {
274 niceName.setTo(arg + 12);
275 } else if (strncmp(arg, "--", 2) != 0) {
276 className.setTo(arg);
277 break;
278 } else {
279 --i;
280 break;
281 }
282 }
......
309 if (startSystemServer) {
310 args.add(String8("start-system-server"));
311 }
......
331 if (!niceName.isEmpty()) {
332 runtime.setArgv0(niceName.string(), true /* setProcName */);
333 }
334
335 if (zygote) {
336 runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
337 } else if (className) {
338 runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
339 } else {
340 fprintf(stderr, "Error: no class name or --zygote supplied.\n");
341 app_usage();
342 LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
343 }
344 }
Let's take init.zygote64.rc
this as an example:
1 service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
2 class main
3 priority -20
4 user root
5 group root readproc reserved_disk
6 socket zygote stream 660 root system
7 socket usap_pool_primary stream 660 root system
8 onrestart write /sys/android_power/request_state wake
9 onrestart write /sys/power/state on
10 onrestart restart audioserver
11 onrestart restart cameraserver
12 onrestart restart media
13 onrestart restart netd
14 onrestart restart wificond
15 writepid /dev/cpuset/foreground/tasks
Mainly this script command:service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
In fact, it will be divided into:
service: service identifier
zygote: indicates the name of the service to be opened
/system/bin/app_process64: the path corresponding to the service
-Xzygote: as a parameter required for virtual machine startup, startVm in AndroidRuntime.cpp () calls JNI_CreateJavaVM to use
/system/bin: represents the directory where the virtual machine program is located, because app_process may not be in the same directory as the virtual machine, so app_process needs to know the directory where the virtual machine is located – zygote: specify the ZygoteInit class as the entry,
otherwise Need to specify the class name that needs to be executed
--start-system-server: only available when there is a --zygote parameter, telling ZygoteInit that the first process hatched after startup is SystemServer
"--zygote"
Hit in the first if ,zygote变量
set it totrue
indicate to startzygote进程
, and进程名
change it tozygote
orzygote64
- Hit in the second if
"--start-system-server"
,startSystemServer变量
set totrue
indicate to startSystemServer进程
app_main.cpp
main方法
After the execution of the ZygoteInit
has been runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
started by
2.2 AndroidRuntime starts ZygoteInit
Among them runtime
is AndroidRuntime
the class, let's take a AndroidRuntime
look start方法
:
/frameworks/base/core/jni/AndroidRuntime.cpp
1112 /*
1113 * Start the Android runtime. This involves starting the virtual machine
1114 * and calling the "static void main(String[] args)" method in the class
1115 * named by "className".
1116 *
1117 * Passes the main function two arguments, the class name and the specified
1118 * options string.
1119 */
1120 void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
1121 {
......
1164 /* start the virtual machine */
1165 JniInvocation jni_invocation;
1166 jni_invocation.Init(NULL);
1167 JNIEnv* env;
1168 if (startVm(&mJavaVM, &env, zygote) != 0) {
1169 return;
1170 }
1171 onVmCreated(env);
1172
1173 /*
1174 * Register android functions.
1175 */
1176 if (startReg(env) < 0) {
1177 ALOGE("Unable to register all android natives\n");
1178 return;
1179 }
......
1203
1204 /*
1205 * Start VM. This thread becomes the main thread of the VM, and will
1206 * not return until the VM exits.
1207 */
1208 char* slashClassName = toSlashClassName(className != NULL ? className : "");
1209 jclass startClass = env->FindClass(slashClassName);
1210 if (startClass == NULL) {
1211 ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
1212 /* keep going */
1213 } else {
1214 jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
1215 "([Ljava/lang/String;)V");
1216 if (startMeth == NULL) {
1217 ALOGE("JavaVM unable to find main() in '%s'\n", className);
1218 /* keep going */
1219 } else {
1220 env->CallStaticVoidMethod(startClass, startMeth, strArray);
1226 }
1227 }
......
1235 }
After some registration of the virtual machine and JNI method, the CallStaticVoidMethod
passed class name is called main函数
. The class name we passed is com.android.internal.os.ZygoteInit
the main three things in AndroidRuntime:
- startVm() creates a virtual machine
- startReg() dynamically registers java to call native jni
- Reflection calls ZygoteInit's main()
2.3 ZygoteInit initialization
AndroidRuntime
Created , and by going Native层
to the entry from the Android startup /frameworks/base/core/java/com/android/internal/os/ZygoteInit.javaZygote
AndroidRuntime.start()
Native层
Java层ZygoteInit的main()
ZygoteInit的main()方法
第一个Java进程主方法
818 @UnsupportedAppUsage
819 public static void main(String argv[]) {
820 ZygoteServer zygoteServer = null;
......
833 Runnable caller;
834 try {
......
847 boolean startSystemServer = false;
848 String zygoteSocketName = "zygote";
849 String abiList = null;
850 boolean enableLazyPreload = false;
851 for (int i = 1; i < argv.length; i++) {
852 if ("start-system-server".equals(argv[i])) {
853 startSystemServer = true;
854 } else if ("--enable-lazy-preload".equals(argv[i])) {
855 enableLazyPreload = true;
856 } else if (argv[i].startsWith(ABI_LIST_ARG)) {
857 abiList = argv[i].substring(ABI_LIST_ARG.length());
858 } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
859 zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
860 } else {
861 throw new RuntimeException("Unknown command line argument: " + argv[i]);
862 }
863 }
864
865 final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);
866
867 if (abiList == null) {
868 throw new RuntimeException("No ABI list supplied.");
869 }
870
871 // In some configurations, we avoid preloading resources and classes eagerly.
872 // In such cases, we will preload things prior to our first fork.
873 if (!enableLazyPreload) {
......
877 preload(bootTimingsTraceLog);
......
881 } else {
882 Zygote.resetNicePriority();
883 }
......
896 Zygote.initNativeState(isPrimaryZygote);
897
898 ZygoteHooks.stopZygoteNoThreadCreation();
899
900 zygoteServer = new ZygoteServer(isPrimaryZygote);
901
902 if (startSystemServer) {
903 Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
904
905 // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
906 // child (system_server) process.
907 if (r != null) {
908 r.run();
909 return;
910 }
911 }
912
913 Log.i(TAG, "Accepting command socket connections");
914
915 // The select loop returns early in the child process after a fork and
916 // loops forever in the zygote.
917 caller = zygoteServer.runSelectLoop(abiList);
918 } catch (Throwable ex) {
919 Log.e(TAG, "System zygote died with exception", ex);
920 throw ex;
921 } finally {
922 if (zygoteServer != null) {
923 zygoteServer.closeServerSocket();
924 }
925 }
926
927 // We're in the child process and have exited the select loop. Proceed to execute the
928 // command.
929 if (caller != null) {
930 caller.run();
931 }
932 }
Mainly did 3 things:
preload()预先加载系统资源,如系统类、资源、系统共享库等
创建 ZygoteServer,其实就是 ServerSocket 循环等待通知 fork 子进程
创建 SystemServer进程
2.3.1 preload()
preload()
: Resource preparation, including class loading, resource loading, etc.
135 static void preload(TimingsTraceLog bootTimingsTraceLog) {
138 beginPreload();
141 preloadClasses();
144 cacheNonBootClasspathClassLoaders();
147 preloadResources();
150 nativePreloadAppProcessHALs();
153 maybePreloadGraphicsDriver();
155 preloadSharedLibraries();
156 preloadTextResources();
157 // Ask the WebViewFactory to do any initialization that must run in the zygote process,
158 // for memory sharing purposes.
159 WebViewFactory.prepareWebViewInZygote();
160 endPreload();
161 warmUpJcaProviders();
164 sPreloadComplete = true;
165 }
......
244 /**
245 * Performs Zygote process initialization. Loads and initializes commonly used classes.
246 *
247 * Most classes only cause a few hundred bytes to be allocated, but a few will allocate a dozen
248 * Kbytes (in one case, 500+K).
249 */
250 private static void preloadClasses() {
251 final VMRuntime runtime = VMRuntime.getRuntime();
252
253 InputStream is;
254 try {
255 is = new FileInputStream(PRELOADED_CLASSES);
256 } catch (FileNotFoundException e) {
257 Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
258 return;
259 }
260
261 Log.i(TAG, "Preloading classes...");
262 long startTime = SystemClock.uptimeMillis();
263
264 // Drop root perms while running static initializers.
265 final int reuid = Os.getuid();
266 final int regid = Os.getgid();
267
268 // We need to drop root perms only if we're already root. In the case of "wrapped"
269 // processes (see WrapperInit), this function is called from an unprivileged uid
270 // and gid.
271 boolean droppedPriviliges = false;
272 if (reuid == ROOT_UID && regid == ROOT_GID) {
273 try {
274 Os.setregid(ROOT_GID, UNPRIVILEGED_GID);
275 Os.setreuid(ROOT_UID, UNPRIVILEGED_UID);
276 } catch (ErrnoException ex) {
277 throw new RuntimeException("Failed to drop root", ex);
278 }
279
280 droppedPriviliges = true;
281 }
282
283 // Alter the target heap utilization. With explicit GCs this
284 // is not likely to have any effect.
285 float defaultUtilization = runtime.getTargetHeapUtilization();
286 runtime.setTargetHeapUtilization(0.8f);
287
288 try {
289 BufferedReader br =
290 new BufferedReader(new InputStreamReader(is), Zygote.SOCKET_BUFFER_SIZE);
291
292 int count = 0;
293 String line;
294 while ((line = br.readLine()) != null) {
295 // Skip comments and blank lines.
296 line = line.trim();
297 if (line.startsWith("#") || line.equals("")) {
298 continue;
299 }
300
301 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
302 try {
303 if (false) {
304 Log.v(TAG, "Preloading " + line + "...");
305 }
306 // Load and explicitly initialize the given class. Use
307 // Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups
308 // (to derive the caller's class-loader). Use true to force initialization, and
309 // null for the boot classpath class-loader (could as well cache the
310 // class-loader of this class in a variable).
311 Class.forName(line, true, null);
312 count++;
313 } catch (ClassNotFoundException e) {
314 Log.w(TAG, "Class not found for preloading: " + line);
315 } catch (UnsatisfiedLinkError e) {
316 Log.w(TAG, "Problem preloading " + line + ": " + e);
317 } catch (Throwable t) {
318 Log.e(TAG, "Error preloading " + line + ".", t);
319 if (t instanceof Error) {
320 throw (Error) t;
321 }
322 if (t instanceof RuntimeException) {
323 throw (RuntimeException) t;
324 }
325 throw new RuntimeException(t);
326 }
327 Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
328 }
329
330 Log.i(TAG, "...preloaded " + count + " classes in "
331 + (SystemClock.uptimeMillis() - startTime) + "ms.");
332 } catch (IOException e) {
333 Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
334 } finally {
335 IoUtils.closeQuietly(is);
336 // Restore default.
337 runtime.setTargetHeapUtilization(defaultUtilization);
338
339 // Fill in dex caches with classes, fields, and methods brought in by preloading.
340 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadDexCaches");
341 runtime.preloadDexCaches();
342 Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
343
344 // Bring back root. We'll need it later if we're in the zygote.
345 if (droppedPriviliges) {
346 try {
347 Os.setreuid(ROOT_UID, ROOT_UID);
348 Os.setregid(ROOT_GID, ROOT_GID);
349 } catch (ErrnoException ex) {
350 throw new RuntimeException("Failed to restore root", ex);
351 }
352 }
353 }
354 }
......
382 /**
383 * Load in commonly used resources, so they can be shared across processes.
384 *
385 * These tend to be a few Kbytes, but are frequently in the 20-40K range, and occasionally even
386 * larger.
387 */
388 private static void preloadResources() {
389 final VMRuntime runtime = VMRuntime.getRuntime();
390
391 try {
392 mResources = Resources.getSystem();
393 mResources.startPreloading();
394 if (PRELOAD_RESOURCES) {
395 Log.i(TAG, "Preloading resources...");
396
397 long startTime = SystemClock.uptimeMillis();
398 TypedArray ar = mResources.obtainTypedArray(
399 com.android.internal.R.array.preloaded_drawables);
400 int N = preloadDrawables(ar);
401 ar.recycle();
402 Log.i(TAG, "...preloaded " + N + " resources in "
403 + (SystemClock.uptimeMillis() - startTime) + "ms.");
404
405 startTime = SystemClock.uptimeMillis();
406 ar = mResources.obtainTypedArray(
407 com.android.internal.R.array.preloaded_color_state_lists);
408 N = preloadColorStateLists(ar);
409 ar.recycle();
410 Log.i(TAG, "...preloaded " + N + " resources in "
411 + (SystemClock.uptimeMillis() - startTime) + "ms.");
412
413 if (mResources.getBoolean(
414 com.android.internal.R.bool.config_freeformWindowManagement)) {
415 startTime = SystemClock.uptimeMillis();
416 ar = mResources.obtainTypedArray(
417 com.android.internal.R.array.preloaded_freeform_multi_window_drawables);
418 N = preloadDrawables(ar);
419 ar.recycle();
420 Log.i(TAG, "...preloaded " + N + " resource in "
421 + (SystemClock.uptimeMillis() - startTime) + "ms.");
422 }
423 }
424 mResources.finishPreloading();
425 } catch (RuntimeException e) {
426 Log.w(TAG, "Failure preloading resources", e);
427 }
428 }
preloadClasses()
The method is mainly to read the class name that needs to be preloaded from the /system/etc/preloaded-classes file, and then load the class into memory through Class.forname, and execute some of the static methods (Java's class loading mechanism ). The focus here is the preloaded-classes file, which generally uses Android native files, and its path is in frameworks/base/config/preloaded-classes.
preloadResources()
The method mainly does the following things:
1. In the Resources.startPreloading() method, call the updateConfiguration() method to create a Configuration for the system, which is some configuration sources for subsequent applications and systems.
2. Obtain the preloaded drawables resources from preloaded_drawables and load them into memory. The preloaded_drawables field is defined in frameworks/base/core/res/res/values/arrays.xml.
3. Obtain the preloaded color resource from preloaded_color_state_lists and load it into memory. The preloaded_color_state_lists field is also defined in frameworks/base/core/res/res/values/arrays.xml.
4. If the free window mode is supported, the preloaded freeform drawables defined in the preloaded_freeform_multi_window_drawables field are also loaded.
2.3.2 ZygoteServer
The ZygoteServer object is instantiated in the main method of ZygoteInit, and its runSelectLoop()
methods are called closeServerSocket()
.
/frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
48 class ZygoteServer {
.......
88 /**
89 * Listening socket that accepts new server connections.
90 */
91 private LocalServerSocket mZygoteSocket;
......
142 /**
143 * Initialize the Zygote server with the Zygote server socket, USAP pool server socket, and USAP
144 * pool event FD.
145 *
146 * @param isPrimaryZygote If this is the primary Zygote or not.
147 */
148 ZygoteServer(boolean isPrimaryZygote) {
151 if (isPrimaryZygote) {
152 mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
153 mUsapPoolSocket =
154 Zygote.createManagedSocketFromInitSocket(
155 Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);
156 } else {
157 mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME);
158 mUsapPoolSocket =
159 Zygote.createManagedSocketFromInitSocket(
160 Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);
161 }
166 }
......
176 /**
177 * Registers a server socket for zygote command connections. This opens the server socket
178 * at the specified name in the abstract socket namespace.
179 */
180 void registerServerSocketAtAbstractName(String socketName) {
181 if (mZygoteSocket == null) {
182 try {
183 mZygoteSocket = new LocalServerSocket(socketName);
184 mCloseSocketFd = false;
185 } catch (IOException ex) {
186 throw new RuntimeException(
187 "Error binding to abstract socket '" + socketName + "'", ex);
188 }
189 }
190 }
......
210 /**
211 * Close and clean up zygote sockets. Called on shutdown and on the
212 * child's exit path.
213 */
214 void closeServerSocket() {
215 try {
216 if (mZygoteSocket != null) {
217 FileDescriptor fd = mZygoteSocket.getFileDescriptor();
218 mZygoteSocket.close();
219 if (fd != null && mCloseSocketFd) {
220 Os.close(fd);
221 }
222 }
223 } catch (IOException ex) {
224 Log.e(TAG, "Zygote: error closing sockets", ex);
225 } catch (ErrnoException ex) {
226 Log.e(TAG, "Zygote: error closing descriptor", ex);
227 }
228
229 mZygoteSocket = null;
230 }
......
368 /**
369 * Runs the zygote process's select loop. Accepts new connections as
370 * they happen, and reads commands from connections one spawn-request's
371 * worth at a time.
372 */
373 Runnable runSelectLoop(String abiList) {
374 ArrayList<FileDescriptor> socketFDs = new ArrayList<FileDescriptor>();
375 ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
376
377 socketFDs.add(mZygoteSocket.getFileDescriptor());
378 peers.add(null);
379
380 while (true) {
381 fetchUsapPoolPolicyPropsWithMinInterval();
382
383 int[] usapPipeFDs = null;
384 StructPollfd[] pollFDs = null;
385
386 // Allocate enough space for the poll structs, taking into account
387 // the state of the USAP pool for this Zygote (could be a
388 // regular Zygote, a WebView Zygote, or an AppZygote).
389 if (mUsapPoolEnabled) {
390 usapPipeFDs = Zygote.getUsapPipeFDs();
391 pollFDs = new StructPollfd[socketFDs.size() + 1 + usapPipeFDs.length];
392 } else {
393 pollFDs = new StructPollfd[socketFDs.size()];
394 }
395
396 /*
397 * For reasons of correctness the USAP pool pipe and event FDs
398 * must be processed before the session and server sockets. This
399 * is to ensure that the USAP pool accounting information is
400 * accurate when handling other requests like API blacklist
401 * exemptions.
402 */
403
404 int pollIndex = 0;
405 for (FileDescriptor socketFD : socketFDs) {
406 pollFDs[pollIndex] = new StructPollfd();
407 pollFDs[pollIndex].fd = socketFD;
408 pollFDs[pollIndex].events = (short) POLLIN;
409 ++pollIndex;
410 }
411
412 final int usapPoolEventFDIndex = pollIndex;
413
414 if (mUsapPoolEnabled) {
415 pollFDs[pollIndex] = new StructPollfd();
416 pollFDs[pollIndex].fd = mUsapPoolEventFD;
417 pollFDs[pollIndex].events = (short) POLLIN;
418 ++pollIndex;
419
420 for (int usapPipeFD : usapPipeFDs) {
421 FileDescriptor managedFd = new FileDescriptor();
422 managedFd.setInt$(usapPipeFD);
423
424 pollFDs[pollIndex] = new StructPollfd();
425 pollFDs[pollIndex].fd = managedFd;
426 pollFDs[pollIndex].events = (short) POLLIN;
427 ++pollIndex;
428 }
429 }
430
431 try {
432 Os.poll(pollFDs, -1);
433 } catch (ErrnoException ex) {
434 throw new RuntimeException("poll failed", ex);
435 }
436
437 boolean usapPoolFDRead = false;
438
439 while (--pollIndex >= 0) {
440 if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
441 continue;
442 }
443
444 if (pollIndex == 0) {
445 // Zygote server socket
446
447 ZygoteConnection newPeer = acceptCommandPeer(abiList);
448 peers.add(newPeer);
449 socketFDs.add(newPeer.getFileDescriptor());
450
451 } else if (pollIndex < usapPoolEventFDIndex) {
452 // Session socket accepted from the Zygote server socket
453
454 try {
455 ZygoteConnection connection = peers.get(pollIndex);
456 final Runnable command = connection.processOneCommand(this);
457
458 // TODO (chriswailes): Is this extra check necessary?
459 if (mIsForkChild) {
460 // We're in the child. We should always have a command to run at this
461 // stage if processOneCommand hasn't called "exec".
462 if (command == null) {
463 throw new IllegalStateException("command == null");
464 }
465
466 return command;
467 } else {
468 // We're in the server - we should never have any commands to run.
469 if (command != null) {
470 throw new IllegalStateException("command != null");
471 }
472
473 // We don't know whether the remote side of the socket was closed or
474 // not until we attempt to read from it from processOneCommand. This
475 // shows up as a regular POLLIN event in our regular processing loop.
476 if (connection.isClosedByPeer()) {
477 connection.closeSocket();
478 peers.remove(pollIndex);
479 socketFDs.remove(pollIndex);
480 }
481 }
482 } catch (Exception e) {
483 if (!mIsForkChild) {
484 // We're in the server so any exception here is one that has taken place
485 // pre-fork while processing commands or reading / writing from the
486 // control socket. Make a loud noise about any such exceptions so that
487 // we know exactly what failed and why.
488
489 Slog.e(TAG, "Exception executing zygote command: ", e);
490
491 // Make sure the socket is closed so that the other end knows
492 // immediately that something has gone wrong and doesn't time out
493 // waiting for a response.
494 ZygoteConnection conn = peers.remove(pollIndex);
495 conn.closeSocket();
496
497 socketFDs.remove(pollIndex);
498 } else {
499 // We're in the child so any exception caught here has happened post
500 // fork and before we execute ActivityThread.main (or any other main()
501 // method). Log the details of the exception and bring down the process.
502 Log.e(TAG, "Caught post-fork exception in child process.", e);
503 throw e;
504 }
505 } finally {
506 // Reset the child flag, in the event that the child process is a child-
507 // zygote. The flag will not be consulted this loop pass after the Runnable
508 // is returned.
509 mIsForkChild = false;
510 }
511 } else {
512 // Either the USAP pool event FD or a USAP reporting pipe.
513
514 // If this is the event FD the payload will be the number of USAPs removed.
515 // If this is a reporting pipe FD the payload will be the PID of the USAP
516 // that was just specialized.
517 long messagePayload = -1;
518
519 try {
520 byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES];
521 int readBytes = Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);
522
523 if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) {
524 DataInputStream inputStream =
525 new DataInputStream(new ByteArrayInputStream(buffer));
526
527 messagePayload = inputStream.readLong();
528 } else {
529 Log.e(TAG, "Incomplete read from USAP management FD of size "
530 + readBytes);
531 continue;
532 }
533 } catch (Exception ex) {
534 if (pollIndex == usapPoolEventFDIndex) {
535 Log.e(TAG, "Failed to read from USAP pool event FD: "
536 + ex.getMessage());
537 } else {
538 Log.e(TAG, "Failed to read from USAP reporting pipe: "
539 + ex.getMessage());
540 }
541
542 continue;
543 }
544
545 if (pollIndex > usapPoolEventFDIndex) {
546 Zygote.removeUsapTableEntry((int) messagePayload);
547 }
548
549 usapPoolFDRead = true;
550 }
551 }
552
553 // Check to see if the USAP pool needs to be refilled.
554 if (usapPoolFDRead) {
555 int[] sessionSocketRawFDs =
556 socketFDs.subList(1, socketFDs.size())
557 .stream()
558 .mapToInt(fd -> fd.getInt$())
559 .toArray();
560
561 final Runnable command = fillUsapPool(sessionSocketRawFDs);
562
563 if (command != null) {
564 return command;
565 }
566 }
567 }
568 }
569 }
Mainly do 3 things:
创建ZygoteServer
: ZygoteServer is initialized, and ServerSocket is initialized internally, which provides Zygote with communication capabilities.zygoteServer.runSelectLoop()
: When the zygote process returns to the main() method and executes, from the name and comments, this method should be an infinite loop, which is a method of continuously executing commands in a loop. It mainly does two things:
1. The loop rebuilds the list of listening files, mainly the socket file of ZygoteServer (the socket of ZygoteServer and the socket connected by other application processes) and the usap file node (at present, it seems that zygote is not used by default, its function is unknown, and no analysis is performed).
2. Listen to the file list and get command execution from it.zygoteServer.closeServerSocket()
: After the loop, close the ServerSocket
2.3.3 Create SystemServer process
718 /**
719 * Prepare the arguments and forks for the system server process.
720 *
721 * @return A {@code Runnable} that provides an entrypoint into system_server code in the child
722 * process; {@code null} in the parent.
723 */
724 private static Runnable forkSystemServer(String abiList, String socketName,
725 ZygoteServer zygoteServer) {
726 long capabilities = posixCapabilitiesAsBits(
727 OsConstants.CAP_IPC_LOCK,
728 OsConstants.CAP_KILL,
729 OsConstants.CAP_NET_ADMIN,
730 OsConstants.CAP_NET_BIND_SERVICE,
731 OsConstants.CAP_NET_BROADCAST,
732 OsConstants.CAP_NET_RAW,
733 OsConstants.CAP_SYS_MODULE,
734 OsConstants.CAP_SYS_NICE,
735 OsConstants.CAP_SYS_PTRACE,
736 OsConstants.CAP_SYS_TIME,
737 OsConstants.CAP_SYS_TTY_CONFIG,
738 OsConstants.CAP_WAKE_ALARM,
739 OsConstants.CAP_BLOCK_SUSPEND
740 );
741 /* Containers run without some capabilities, so drop any caps that are not available. */
742 StructCapUserHeader header = new StructCapUserHeader(
743 OsConstants._LINUX_CAPABILITY_VERSION_3, 0);
744 StructCapUserData[] data;
745 try {
746 data = Os.capget(header);
747 } catch (ErrnoException ex) {
748 throw new RuntimeException("Failed to capget()", ex);
749 }
750 capabilities &= ((long) data[0].effective) | (((long) data[1].effective) << 32);
751
752 /* Hardcoded command line to start the system server */
753 String args[] = {
754 "--setuid=1000",
755 "--setgid=1000",
756 "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
757 + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
758 "--capabilities=" + capabilities + "," + capabilities,
759 "--nice-name=system_server",
760 "--runtime-args",
761 "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
762 "com.android.server.SystemServer",
763 };
764 ZygoteArguments parsedArgs = null;
765
766 int pid;
767
768 try {
769 parsedArgs = new ZygoteArguments(args);
770 Zygote.applyDebuggerSystemProperty(parsedArgs);
771 Zygote.applyInvokeWithSystemProperty(parsedArgs);
772
773 boolean profileSystemServer = SystemProperties.getBoolean(
774 "dalvik.vm.profilesystemserver", false);
775 if (profileSystemServer) {
776 parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
777 }
778
779 /* Request to fork the system server process */
780 pid = Zygote.forkSystemServer(
781 parsedArgs.mUid, parsedArgs.mGid,
782 parsedArgs.mGids,
783 parsedArgs.mRuntimeFlags,
784 null,
785 parsedArgs.mPermittedCapabilities,
786 parsedArgs.mEffectiveCapabilities);
787 } catch (IllegalArgumentException ex) {
788 throw new RuntimeException(ex);
789 }
790
791 /* For child process */
792 if (pid == 0) {
793 if (hasSecondZygote(abiList)) {
794 waitForSecondaryZygote(socketName);
795 }
796
797 zygoteServer.closeServerSocket();
798 return handleSystemServerProcess(parsedArgs);
799 }
800
801 return null;
802 }
forkSystemServer()
:
①Call the Zygote.forkSystemServer() method to fork a new process.
②The child process after fork() is the SystemServer process, then wait for the startup of zygote to complete, and execute the real SystemServer code.
2.4 Zygote startup flowchart
3 summary
All processes are created by Zygote, and zygote is mainly used for incubation system_server进程
and processing 应用程序进程
. After incubating the first process system_server, by runSelectLoop
waiting and processing the message, the split application process is still controlled by the system_server, 等待 AMS 给他发消息(告诉 zygote 创建进程)
such as creating a child process when the app starts.
From AndroidRuntime to ZygoteInit, it is mainly divided into three major processes:
1.
创建虚拟机——startVm()
: Call the JNI virtual machine creation function
2.注册JNI函数——startReg()
: The virtual machine has been created before, and here are some JNI functions registered for this virtual machine (the functions used in the subsequent java world are native implementations, and these functions need to be registered in advance) 3.
Here When it is necessary执行CallStaticViodMethod
, through this function, you will enter the java world carefully crafted by android. This function will call the main function of com.android.internal.os.ZygoteInit
Entering the Java world in the ZygoteInit.main function, there are 4 key steps:
1.
预加载类和资源——preload()
Mainly preloadClasses and preloadResources, where preloadClasses is generally a class whose loading time exceeds 1250ms, so it needs to be preloaded in zygote
2.建立IPC通信服务——初始化ZygoteServer,内部初始化了ZygoteSocket
The communication between zygote and other programs in the system does not use Binder, but uses Socket based on AF_UNIX type. It is to establish this Socket
3.启动system_server——forkSystemServer()
This function will create the process system_server where the system Service resides in the Java world. This process is the core of the framework and the first process hatched by zygote. If it dies, it causes the zygote to commit suicide.
4.等待请求——runSelectLoop()
After zygote returns from startSystemServer, it will enter the fourth key function runSelectLoop. In the first function ZygoteServer, a Socket for IPC is registered and will be used here. Here Zygote adopts an efficient I/O multiplexing mechanism , to ensure that it sleeps when there is no client request or data processing, otherwise it responds to the client's request.等待 AMS 给他发消息(告诉 zygote 创建进程)
. At this time, zygote has completed the initial work in the java world, calls runSelectLoop and starts to sleep, and will wake up at any time when receiving a request or data processing, and continue to work.
4 interview questions
1 What is the role of the init process
The init process plays a link between the preceding and the following. Android itself is based on Linux. The init process is the first process of the user space in the Linux system. The init process belongs to a daemon process. To be precise, it is the first process controlled by the user in the Linux system. Its process number is 1 (the process number is 0 is the kernel process), and its life cycle runs through the entire Linux kernel. always. The common originator of all other processes in Android is the init process.
The init entry function of Android Q (10.0) is adjusted from the original init.cpp to main.cpp, which separates the operations of each stage and makes the code more concise.
As the No. 1 process of the Son of Heaven, init has been given many important responsibilities, which are mainly divided into three stages:
- The main work of the first stage of the init process is
挂载分区
to create device nodes and some key directories, initialize the log output system, and enable SELinux security policies.- The main work of the second stage of the init process is
初始化属性系统
to analyze the matching rules of SELinux, process the termination signal of the child process, and start the system attribute service. It can be said that each item is very critical. If the first stage is to prepare for the attribute system and SELinux, then The second stage is to actually implement these functions.- The third stage of init is mainly
解析init.rc来启动其他进程
to enter an infinite loop and proceed子进程实时监控(守护)
.
The third stage starts other processes through initrc, such as our common ones 启动Zygote进程
, 启动SeviceManager进程
etc.
2 What is the original process of the Zygote process (or the origin of the Zygote process)
At the beginning app_process
, Zygote was started when the init process started, and then it app_main.cpp
was modified to Zygote.
3 Is Zygote in kernel space or user space?
Because the init process is created in user space, and Zygote is created and started by the init process, soZygote是在用户空间。
4 Why does Zygote need to use Socket communication instead of Binder
Zygote is an important process in Android, it is the parent process that starts the application process. Zygote uses Socket to communicate with the application process instead of using the IPC mechanism Binder in Android. This is because Socket and Binder have different advantages and disadvantages, and using Socket in the Zygote process can better meet the needs of the Zygote process.
Zygote 用 binder 通信会导致死锁
Assuming that Zygote uses Binder communication, because Binder supports multi-threading, there are concurrency problems, and the solution to concurrency problems is to add locks. If the process fork is running under multi-threading, Binder may wait for the lock under the lock mechanism. deadlock.Zygote 用 binder 通信会导致读写错误
The fundamental reason is that when a new ProcessState is used for Binder communication, mmap needs to apply for a piece of memory to provide to the kernel for data exchange. And if it is directly forked, when the child process is communicating with the binder, the kernel will continue to use the address requested by the parent process to write data, and at this time it will trigger the child process COW (Copy on Write), which will cause the address space to be remapped. The child process also tries to access the address of the previous parent process mmap, which will cause SIGSEGV and SEGV_MAPERR segment faults.Zygote初始化时,Binder还没开始初始化。
Socket具有良好的跨平台性
, to be able to communicate between different operating systems and languages. This is very important for the Zygote process as it needs to run on different devices and architectures and communicate with different application processes. Using Socket can make the Zygote process more flexible and scalable, because it does not need to consider the specific restrictions and requirements brought by Binder.Socket具有简单的API和易于使用的特点
. The Zygote process needs to start quickly and establish communication with the application process. Socket provides a fast and reliable communication method, and it is easy to implement using the Socket API. In contrast, Binder requires more configuration and maintenance work, which may add unnecessary complexity and overhead to the Zygote process.Socket在数据传输时具有更低的延迟和更高的吞吐量
, which is very important for the Zygote process. The Zygote process needs to start the application process in a short period of time, and needs to transmit a large amount of data and codes. The high performance and low latency of Socket make it a better choice.
In short, the Zygote process uses Socket instead of Binder is a choice based on its advantages and needs. Although Binder plays an important role in Android, in some cases, using Socket can provide better performance and greater flexibility. Furthermore, Binder was not mature at the beginning, and team members preferred to use Socket for inter-process communication. Later, a lot of optimizations were done to make Binder communication mature and stable.
5 Will each App load system resources and system classes?
The role of the zygote process:
1. Create a Socket on the Service side, open a ServerSocket to communicate with other processes.
2. Load system classes and system resources.
3. Start the System Server process
After the Zygote process preloads system resources, it then incubates other virtual machine processes, and then shares virtual machine memory and framework layer resources (共享内存)
, which greatly improves the startup and running speed of applications.
6 What does PMS do, how do you understand PMS
Package management, package parsing, result caching, and query interfaces are provided.
/data/app
folders traversed- unzip
apk
file - dom parses
AndroidManifest.xml
the file.
7 Why there is AMS The role of AMS
- Query PMS
- reflection generated object
- Manage the Activity lifecycle
AMS Cache Center:ActivityThread
8 How AMS manages Activity, exploring the execution principle of AMS
Activity is responsible for describing the process and state of its life cycle on the application side ActivityClientRecord
, but in the end these processes and states are ActivityManagerService(以下简称AMS)
managed and controlled
BroadcastRecord
: Describes the BroadcastReceiver of the application process, whichBroadcastQueue
is managed by.ServiceRecord
: Describes the Service service component, which isActiveServices
responsible for management.ContentProviderRecord
: Describes the ContentProvider content provider,ProviderMap
managed by.ActivityRecord
: Used to describe the Activity,ActivityStackSupervisor
managed by.