[Android Framework Series] Chapter 3 Zygote process related

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.rcThe 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.rcthis 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

  1. "--zygote"Hit in the first if , zygote变量set it to trueindicate to start zygote进程, and 进程名change it to zygoteorzygote64
  2. Hit in the second if "--start-system-server", startSystemServer变量set to trueindicate to startSystemServer进程

app_main.cppmain方法After the execution of the ZygoteInithas been runtime.start("com.android.internal.os.ZygoteInit", args, zygote);started by

2.2 AndroidRuntime starts ZygoteInit

Among them runtimeis AndroidRuntimethe class, let's take a AndroidRuntimelook 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 CallStaticVoidMethodpassed class name is called main函数. The class name we passed is com.android.internal.os.ZygoteInit
the main three things in AndroidRuntime:

  1. startVm() creates a virtual machine
  2. startReg() dynamically registers java to call native jni
  3. Reflection calls ZygoteInit's main()

2.3 ZygoteInit initialization

AndroidRuntimeCreated , and by going Native层to the entry from the Android startup /frameworks/base/core/java/com/android/internal/os/ZygoteInit.javaZygoteAndroidRuntime.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:

  1. preload()预先加载系统资源,如系统类、资源、系统共享库等
  2. 创建 ZygoteServer,其实就是 ServerSocket 循环等待通知 fork 子进程
  3. 创建 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:

  1. 创建ZygoteServer: ZygoteServer is initialized, and ServerSocket is initialized internally, which provides Zygote with communication capabilities.
  2. 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.
  3. 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

insert image description here

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 runSelectLoopwaiting 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:

  1. 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.
  2. 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.
  3. 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.cppwas 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.

  1. 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.
  2. 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.
  3. Zygote初始化时,Binder还没开始初始化。
  4. 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.
  5. 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.
  6. 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.

  1. /data/appfolders traversed
  2. unzip apkfile
  3. dom parses AndroidManifest.xmlthe file.

7 Why there is AMS The role of AMS

  1. Query PMS
  2. reflection generated object
  3. 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

  1. BroadcastRecord: Describes the BroadcastReceiver of the application process, which BroadcastQueueis managed by.
  2. ServiceRecord: Describes the Service service component, which is ActiveServicesresponsible for management.
  3. ContentProviderRecord: Describes the ContentProvider content provider, ProviderMapmanaged by.
  4. ActivityRecord: Used to describe the Activity, ActivityStackSupervisormanaged by.

Guess you like

Origin blog.csdn.net/u010687761/article/details/131404918