[Serie Android Framework] Capítulo 3 relacionado con el proceso Zygote

1 Introducción al cigoto

Zygote es el proceso más importante en Android Zygote进程和Init进程、SystemServer进程是Android最重要的三大进程. Zygote es el proceso central para crear un nuevo proceso en el sistema Android. Es responsable de iniciar la máquina virtual Dalvik, cargar algunos recursos y clases del sistema necesarios, iniciar el proceso system_server y luego esperar para procesar las solicitudes de la aplicación.
En el sistema Android, el proceso Zygote trama el proceso de solicitud y el proceso Init inicia el proceso Zygote. El proceso Zygote creará una instancia de máquina virtual Dalvik cuando se inicie, y cada vez que genere un nuevo proceso de aplicación, copiará la instancia de la máquina virtual Dalvik al nuevo proceso de aplicación, de modo que cada proceso de aplicación tenga una máquina virtual Dalvik independiente. instancia.

Clases involucradas en 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 inicio del cigoto

Este artículo se basa en Android10(Q)el análisis del código fuente.

2.1 El proceso de inicio analiza el script init.rc

Zygote se inicia mediante init进程el script de análisis . init.rcEl script se pasa app_process的main方法para la segmentación y la lógica correspondiente se realiza de acuerdo con el comando de cadena.

Ahora que la máquina está dividida en 32 y 64 bits, el script de inicio de Zygote init.rc también es diferente:

  • init.zygote32.rc: el programa de ejecución correspondiente al proceso cigoto es app_process (modo puro de 32 bits)
  • init.zygote64.rc: el programa de ejecución correspondiente al proceso cigoto es app_process64 (modo puro de 64 bits)
  • init.zygote32_64.rc: inicia dos procesos de cigoto, los programas de ejecución correspondientes son app_process32 (modo principal), app_process64
  • init.zygote64_32.rc: inicia dos procesos de cigoto, los programas de ejecución correspondientes son app_process64 (modo principal), app_process32

El programa que ejecutará zygote es system/bin/app_processy su código fuente está aquí app_main.cpp. Echemos un vistazo a cómo app_main maneja los comandos de script:
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  }

Tomemos init.zygote64.rcesto como ejemplo:

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

Principalmente este comando de script:service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server

De hecho, se dividirá en:
servicio: identificador de servicio
zygote: indica el nombre del servicio a abrir
/system/bin/app_process64: la ruta correspondiente al servicio
-Xzygote: como parámetro requerido para el inicio de la máquina virtual, startVm en AndroidRuntime.cpp () llama a JNI_CreateJavaVM para usar
/system/bin: representa el directorio donde se encuentra el programa de la máquina virtual, porque app_process puede no estar en el mismo directorio que la máquina virtual, por lo que app_process necesita conocer el directorio donde se encuentra la máquina virtual. donde se encuentra la máquina – zygote: especifique la clase ZygoteInit como entrada;
de lo contrario, debe especificar el nombre de la clase que debe ejecutarse
--start-system-server: solo está disponible cuando hay un parámetro --zygote, que le indica a ZygoteInit que el primero El proceso que se genera después del inicio es SystemServer

  1. "--zygote"Presiona el primer if , zygote变量configúralo para trueindicar inicio zygote进程y 进程名cámbialo a zygoteozygote64
  2. Golpea en el segundo si "--start-system-server", startSystemServer变量configurado para trueindicar que comienzaSystemServer进程

app_main.cppUna vez iniciada la main方法ejecución delZygoteInitruntime.start("com.android.internal.os.ZygoteInit", args, zygote);

2.2 AndroidRuntime inicia ZygoteInit

Entre ellos runtimeestá AndroidRuntimela clase, echemos un AndroidRuntimevistazo 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  }

Después de un cierto registro de la máquina virtual y el método JNI, CallStaticVoidMethodse llama al nombre de clase pasado main函数. El nombre de clase que pasamos son com.android.internal.os.ZygoteInit
las tres cosas principales en AndroidRuntime:

  1. startVm() crea una máquina virtual
  2. startReg() registra dinámicamente Java para llamar a jni nativo
  3. La reflexión llama al main() de ZygoteInit

2.3 Inicialización de ZygoteInit

AndroidRuntimeCreado y yendo Native层a la entrada del inicio de Android /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      }

Principalmente hizo 3 cosas:

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

2.3.1 precarga()

preload(): Preparación de recursos, incluida la carga de clases, la carga de recursos, 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()El método consiste principalmente en leer el nombre de la clase que debe cargarse previamente desde el archivo /system/etc/preloaded-classes y luego cargar la clase en la memoria a través de Class.forname y ejecutar algunos de los métodos estáticos (mecanismo de carga de clases de Java). ). El enfoque aquí es el archivo de clases precargadas, que generalmente usa archivos nativos de Android y su ruta está en frameworks/base/config/preloaded-classes.

preloadResources()El método hace principalmente las siguientes cosas:
1. En el método Resources.startPreloading(), llame al método updateConfiguration() para crear una configuración para el sistema, que son algunas fuentes de configuración para aplicaciones y sistemas posteriores.
2. Obtenga los recursos dibujables precargados de preloaded_drawables y cárguelos en la memoria. El campo preloaded_drawables se define en frameworks/base/core/res/res/values/arrays.xml.
3. Obtenga el recurso de color precargado de preloaded_color_state_lists y cárguelo en la memoria. El campo preloaded_color_state_lists también se define en frameworks/base/core/res/res/values/arrays.xml.
4. Si se admite el modo de ventana libre, también se cargan los elementos dibujables de forma libre precargados definidos en el campo preloaded_freeform_multi_window_drawables.

2.3.2 Servidor Zygote

Se crea una instancia del objeto ZygoteServer en el método principal de ZygoteInit y sus runSelectLoop()métodos se llaman 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  }

Principalmente haz 3 cosas:

  1. 创建ZygoteServer: ZygoteServer se inicializa y ServerSocket se inicializa internamente, lo que proporciona a Zygote capacidades de comunicación.
  2. zygoteServer.runSelectLoop(): Cuando el proceso cigoto regresa al método main() y se ejecuta, según el nombre y los comentarios, este método debe ser un bucle infinito, que es un método para ejecutar comandos continuamente en un bucle. Principalmente hace dos cosas:
    1. El bucle reconstruye la lista de archivos de escucha, principalmente el archivo de socket de ZygoteServer (el socket de ZygoteServer y el socket conectado por otros procesos de aplicación) y el nodo del archivo usap (en la actualidad, parece que zygote no se usa de forma predeterminada, se desconoce su función y no se realiza ningún análisis).
    2. Escuche la lista de archivos y obtenga la ejecución del comando.
  3. zygoteServer.closeServerSocket(): Después del ciclo, cierre ServerSocket

2.3.3 Crear proceso SystemServer

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():
①Llame al método Zygote.forkSystemServer() para bifurcar un nuevo proceso.
②El proceso hijo después de fork () es el proceso SystemServer, luego espere a que se complete el inicio de zygote y ejecute el código real de SystemServer.

2.4 Diagrama de flujo de inicio de Zygote

inserte la descripción de la imagen aquí

3 Resumen

Todos los procesos son creados por Zygote, y el cigoto se utiliza principalmente para la incubación system_server进程y el procesamiento 应用程序进程. Después de incubar el primer proceso system_server, al runSelectLoopesperar y procesar el mensaje, el proceso de aplicación dividida todavía está controlado por system_server, 等待 AMS 给他发消息(告诉 zygote 创建进程)como la creación de un proceso secundario cuando se inicia la aplicación.

Desde AndroidRuntime hasta ZygoteInit, se divide principalmente en tres procesos principales:

1.: 创建虚拟机——startVm()Llame a la función de creación de la máquina virtual JNI
2 注册JNI函数——startReg().: La máquina virtual se creó antes y aquí hay algunas funciones JNI registradas para esta máquina virtual (las funciones utilizadas en el mundo Java posterior son implementaciones nativas y estas funciones deben registrarse con anticipación) 3.
Aquí, cuando sea necesario 执行CallStaticViodMethod, a través de esta función, ingresará al mundo Java cuidadosamente diseñado por Android. Esta función llamará a la función principal de com.android.internal.os.ZygoteInit

Para ingresar al mundo de Java en la función ZygoteInit.main, hay 4 pasos clave:

1. 预加载类和资源——preload()
Principalmente preloadClasses y preloadResources, donde preloadClasses es generalmente una clase cuyo tiempo de carga excede los 1250 ms, por lo que debe precargarse en zygote
2. 建立IPC通信服务——初始化ZygoteServer,内部初始化了ZygoteSocket
La comunicación entre zygote y otros programas en el sistema no usa Binder, pero usa Socket basado en AF_UNIX tipo.Es para establecer este Socket
3.Esta 启动system_server——forkSystemServer()
función creará el proceso system_server donde reside el Servicio del sistema en el mundo Java.Este proceso es el núcleo del marco y el primer proceso generado por el cigoto. Si muere, provoca que el cigoto se suicide.
4. 等待请求——runSelectLoop()
Después de que zygote regrese de startSystemServer, ingresará a la cuarta función clave runSelectLoop. En la primera función ZygoteServer, se registra un socket para IPC y se usará aquí. Aquí Zygote adopta un mecanismo de multiplexación de E/S eficiente para garantizar que duerme cuando no hay solicitud del cliente o procesamiento de datos, de lo contrario responde a la solicitud del cliente. 等待 AMS 给他发消息(告诉 zygote 创建进程). En este momento, zygote ha completado el trabajo inicial en el mundo de Java, llama a runSelectLoop y comienza a dormir, y se despertará en cualquier momento cuando reciba una solicitud o procesamiento de datos y continuará trabajando.

4 preguntas de la entrevista

1 ¿Cuál es el papel del proceso de inicio?

El proceso init juega un vínculo entre lo anterior y lo siguiente. El propio Android está basado en Linux. El proceso init es el primer proceso del espacio de usuario en el sistema Linux. El proceso init pertenece a un proceso demonio, para ser precisos, es el primer proceso controlado por el usuario en el sistema Linux, su número de proceso es 1 (el número de proceso es 0 es el proceso del kernel) y su ciclo de vida se ejecuta a través de todo el kernel de Linux, siempre. El origen común de todos los demás procesos en Android es el proceso de inicio.
La función de entrada de inicio de Android Q (10.0) se ajusta del init.cpp original a main.cpp, lo que separa las operaciones de cada etapa y hace que el código sea más conciso.
Como proceso número uno del Hijo del Cielo, se le han asignado muchas responsabilidades importantes, que se dividen principalmente en tres etapas:

  1. El trabajo principal de la primera etapa del proceso de inicio es 挂载分区crear nodos de dispositivos y algunos directorios clave, inicializar el sistema de salida de registros y habilitar las políticas de seguridad de SELinux.
  2. El trabajo principal de la segunda etapa del proceso de inicio es 初始化属性系统analizar las reglas de coincidencia de SELinux, procesar la señal de terminación del proceso hijo e iniciar el servicio de atributos del sistema. Se puede decir que cada elemento es muy crítico. Si el primero La etapa es prepararse para el sistema de atributos y SELinux, luego la segunda etapa es implementar realmente estas funciones.
  3. La tercera etapa de init es principalmente 解析init.rc来启动其他进程ingresar a un bucle infinito y continuar 子进程实时监控(守护).

La tercera etapa inicia otros procesos a través de initrc, como los nuestros comunes 启动Zygote进程, 启动SeviceManager进程etc.

2 ¿Cuál es el proceso original del proceso Cigoto (o el origen del proceso Cigoto)?

Al principio app_process, Zygote se inició cuando comenzó el proceso de inicio y luego se app_main.cppmodificó a Zygote.

3 ¿Zygote está en el espacio del kernel o en el espacio del usuario?

Debido a que el proceso de inicio se crea en el espacio del usuario, y Zygote es creado e iniciado por el proceso de inicio, entoncesZygote是在用户空间。

4 ¿Por qué Zygote necesita utilizar la comunicación Socket en lugar de Binder?

Zygote es un proceso importante en Android, es el proceso principal que inicia el proceso de solicitud. Zygote usa Socket para comunicarse con el proceso de la aplicación en lugar de usar el mecanismo IPC Binder en Android, porque Socket y Binder tienen diferentes ventajas y desventajas, y el uso de Socket en el proceso Zygote puede satisfacer mejor las necesidades del proceso Zygote.

  1. Zygote 用 binder 通信会导致死锁
    Supongamos que Zygote usa Binder para comunicarse, debido a que Binder admite subprocesos múltiples, hay problemas de concurrencia y la solución al problema de concurrencia es agregar bloqueos. Si la bifurcación del proceso se ejecuta en subprocesos múltiples, Binder puede esperar el bloqueo bajo el mecanismo de bloqueo punto muerto.
  2. Zygote 用 binder 通信会导致读写错误
    La razón fundamental es que cuando se utiliza un nuevo ProcessState para la comunicación de Binder, mmap necesita solicitar una porción de memoria para proporcionarla al kernel para el intercambio de datos. Y si se bifurca directamente, cuando el proceso hijo se comunica con la carpeta, el kernel continuará usando la dirección solicitada por el proceso padre para escribir datos, y en este momento activará el proceso hijo COW (Copiar en escritura). , lo que hará que se reasigne el espacio de direcciones. El proceso hijo también intenta acceder a la dirección del mmap del proceso padre anterior, lo que provocará fallas en los segmentos SIGSEGV y SEGV_MAPERR.
  3. Zygote初始化时,Binder还没开始初始化。
  4. Socket具有良好的跨平台性, para poder comunicarse entre diferentes sistemas operativos y lenguajes. Esto es muy importante para el proceso Zygote, ya que necesita ejecutarse en diferentes dispositivos y arquitecturas y comunicarse con diferentes procesos de aplicación. El uso de Socket puede hacer que el proceso Zygote sea más flexible y escalable, porque no necesita considerar las restricciones y requisitos específicos que trae Binder.
  5. Socket具有简单的API和易于使用的特点. El proceso Zygote debe iniciarse rápidamente y establecer comunicación con el proceso de la aplicación. Socket proporciona un método de comunicación rápido y confiable, y es fácil de implementar utilizando la API de Socket. Por el contrario, Binder requiere más trabajo de configuración y mantenimiento, lo que puede agregar complejidad y gastos generales innecesarios al proceso Zygote.
  6. Socket在数据传输时具有更低的延迟和更高的吞吐量, que es muy importante para el proceso de cigoto. El proceso Zygote necesita iniciar el proceso de solicitud en un corto período de tiempo y necesita transmitir una gran cantidad de datos y códigos. El alto rendimiento y la baja latencia de Socket lo convierten en una mejor opción.

En resumen, el uso de Socket en lugar de Binder en el proceso Zygote es una elección basada en sus ventajas y necesidades. Aunque Binder juega un papel importante en Android, en algunos casos, el uso de Socket puede proporcionar un mejor rendimiento y una mayor flexibilidad. Además, Binder no estaba maduro al principio y los miembros del equipo preferían usar Socket para la comunicación entre procesos. Posteriormente, se realizaron muchas optimizaciones para que la comunicación de Binder fuera madura y estable.

5 ¿Cada aplicación cargará recursos y clases del sistema?

La función del proceso cigoto:
1. Cree un Socket en el lado del Servicio y abra un ServerSocket para comunicarse con otros procesos.
2. Cargar clases del sistema y recursos del sistema.
3. Inicie el proceso del servidor del sistema.

Después de que el proceso Zygote precarga los recursos del sistema, incuba otros procesos de la máquina virtual y luego comparte la memoria de la máquina virtual y los recursos de la capa de marco (共享内存), lo que mejora en gran medida la velocidad de inicio y ejecución de las aplicaciones.

6 ¿Qué hace el síndrome premenstrual? ¿Cómo se entiende el síndrome premenstrual?

Se proporcionan gestión de paquetes, análisis de paquetes, almacenamiento en caché de resultados e interfaces de consulta.

  1. /data/appcarpetas atravesadas
  2. descomprimir apkel archivo
  3. dom analiza AndroidManifest.xmlel archivo.

7 Por qué existe AMS El papel de AMS

  1. Consulta PMS
  2. objeto generado por reflexión
  3. Gestionar el ciclo de vida de la actividad

Centro de caché AMS:ActivityThread

8 Cómo AMS gestiona la actividad, explorando el principio de ejecución de AMS

La actividad es responsable de describir el proceso y el estado de su ciclo de vida en el lado de la aplicación ActivityClientRecord, pero al final estos procesos y estados son ActivityManagerService(以下简称AMS)gestionados y controlados.

  1. BroadcastRecord: Describe el BroadcastReceiver del proceso de solicitud, que BroadcastQueuees administrado por.
  2. ServiceRecord: Describe el componente del servicio Servicio, que es ActiveServicesresponsable de la gestión.
  3. ContentProviderRecord: describe el proveedor de contenido ContentProvider, ProviderMapadministrado por.
  4. ActivityRecord: Se utiliza para describir la actividad, ActivityStackSupervisorgestionada por.

Supongo que te gusta

Origin blog.csdn.net/u010687761/article/details/131404918
Recomendado
Clasificación