【Android Framework系列】第3章 Zygote进程相关

1 Zygote简介

Zygote是Android中最重要的一个进程,Zygote进程和Init进程、SystemServer进程是Android最重要的三大进程Zygote是Android系统创建新进程的核心进程,负责启动Dalvik虚拟机,加载一些必要的系统资源和系统类,启动system_server进程,随后进入等待处理app应用请求。
在Android系统中,应用程序进程都是由Zygote进程孵化出来的,而Zygote进程是由Init进程启动的。Zygote进程在启动时会创建一个Dalvik虚拟机实例,每当它孵化一个新的应用程序进程时,都会将这个Dalvik虚拟机实例复制到新的应用程序进程里面去,从而使得每一个应用程序进程都有一个独立的Dalvik虚拟机实例。

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启动

本文基于Android10(Q)的源码做分析

2.1 init进程解析init.rc脚本

Zygote由init进程解析init.rc脚本启动的。脚本传入app_process的main方法做分割,根据字符串命令做相应逻辑。

现在机器分为32位和64位,Zygote的启动脚本init.rc也各有区别:

  • init.zygote32.rc:zygote进程对应的执行程序是app_process(纯32bit模式)
  • init.zygote64.rc:zygote进程对应的执行程序是app_process64(纯64bit模式)
  • init.zygote32_64.rc:启动两个zygote进程,对应的执行程序分别是app_process32(主模式)、app_process64
  • init.zygote64_32.rc:启动两个zygote进程,对应的执行程序分别是app_process64(主模式)、app_process32

zygote要执行的程序便是system/bin/app_process,它的源代码在app_main.cpp。我们先来看看app_main是如何处理脚本命令:
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  }

我们拿init.zygote64.rc为例:

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

主要是这个脚本命令:service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server

实际上会被分割成:
service:服务标识
zygote:表示要开启的服务名字
/system/bin/app_process64:服务对应的路径
-Xzygote:作为虚拟机启动时所需要的参数,在AndroidRuntime.cpp中的 startVm() 中调用JNI_CreateJavaVM 使用到
/system/bin:代表虚拟机程序所在目录,因为 app_process 可以不和虚拟机在一个目录,所以 app_process 需要知道虚拟机所在的目录
–zygote :指明以 ZygoteInit 类作为入口,否则需要指定需要执行的类名
–start-system-server:仅在有 --zygote 参数时可用,告知 ZygoteInit 启动完毕后孵化出的第一个进程是 SystemServer

  1. 第一个if中"--zygote"命中,zygote变量置为true表示要启动zygote进程,并将进程名改成了zygotezygote64
  2. 第二个if中"--start-system-server"命中,startSystemServer变量置为true表示要启动SystemServer进程

app_main.cppmain方法执行后,ZygoteInit已经通过runtime.start("com.android.internal.os.ZygoteInit", args, zygote);被启动了

2.2 AndroidRuntime启动ZygoteInit

其中runtime就是AndroidRuntime类,我们来看看AndroidRuntimestart方法
/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  }

对虚拟机和JNI方法的一些注册后,通过CallStaticVoidMethod来调用传过来的类名的main函数,我们传递过来的类名是com.android.internal.os.ZygoteInit
AndroidRuntime中主要做了这三件事:

  1. startVm() 创建虚拟机
  2. startReg() 动态注册 java 调用 native 的 jni
  3. 反射调用 ZygoteInit 的 main()

2.3 ZygoteInit初始化

AndroidRuntimeNative层创建了Zygote,并且通过AndroidRuntime.start()Native层转到Java层ZygoteInit的main()入口,ZygoteInit的main()方法是Android启动的第一个Java进程主方法
/frameworks/base/core/java/com/android/internal/os/ZygoteInit.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      }

主要做了3件事:

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

2.3.1 preload()

preload():资源准备,包括类加载,资源加载等

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()方法主要是从/system/etc/preloaded-classes文件中读取需要预加载的类名,然后通过Class.forname将该类加载到内存中,并会执行其中的一些静态方法(Java的类加载机制)。这里的重点是preloaded-classes文件,这个文件一般使用Android原生的文件,其路径在frameworks/base/config/preloaded-classes。

preloadResources()方法主要是做了以下几件事:
1.在Resources.startPreloading()方法中,调用updateConfiguration()方法为系统创建Configuration,这是后面应用和系统的一些配置来源。
2.从preloaded_drawables中获取预加载的drawables资源,并将其加载到内存中。preloaded_drawables字段在frameworks/base/core/res/res/values/arrays.xml中定义。
3.从preloaded_color_state_lists中获取预加载的color资源,并将其加载到内存中,preloaded_color_state_lists字段也是定义在frameworks/base/core/res/res/values/arrays.xml中。
4.如果支持自由窗口模式,则将preloaded_freeform_multi_window_drawables字段中定义的预加载的freeform drawables也加载进来。

2.3.2 ZygoteServer

ZygoteInit的main方法内实例化了ZygoteServer对象,并调用了其中runSelectLoop()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  }

主要做3件事:

  1. 创建ZygoteServer:ZygoteServer初始化,内部对ServerSocket进行了初始化,为Zygote提供了通信的能力。
  2. zygoteServer.runSelectLoop():当zygote进程返回到main()方法后执行,从名字上和注释来看,这个方法应该是一个死循环,是不断进行循环执行命令的方法,主要做了两件事:​
    1.每次循环都重新构建监听文件列表,主要是ZygoteServer的socket文件(ZygoteServer的socket和其他应用进程连接过来的socket)和usap文件节点(目前看来,zygote默认是没有使用,作用未明,不做分析)。
    ​2.监听文件列表,并从中获取命令执行。
  3. zygoteServer.closeServerSocket():在循环后,关闭ServerSocket

2.3.3 创建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()
①调用Zygote.forkSystemServer()方法去fork一个新的进程出来。
②fork()后的子进程是SystemServer进程,则等待zygote的启动完成,并执行真正的SystemServer代码。

2.4 Zygote启动流程图

在这里插入图片描述

3 总结

所有的进程都由Zygote创建,zygote主要用来孵化system_server进程应用程序进程。在孵化出第一个进程system_server后通过runSelectLoop等待并处理消息,分裂应用程序进程仍由system_server控制,等待 AMS 给他发消息(告诉 zygote 创建进程),如app启动时创建子进程。

从AndroidRuntime到ZygoteInit,主要分为3大过程:

1、创建虚拟机——startVm():调用JNI虚拟机创建函数
2、注册JNI函数——startReg():前面已经创建虚拟机,这里给这个虚拟机注册一些JNI函数(后续java世界用到的函数是native实现,这里需要提前注册注册这些函数)
3、此时就要执行CallStaticViodMethod,通过这个函数将进入android精心打造的java世界,这个函数将调用com.android.internal.os.ZygoteInit的main函数

在 ZygoteInit.main函数中进入Java世界,主要有4个关键步骤:

1、预加载类和资源——preload()
主要是preloadClasses和preloadResources,其中preloadClasses一般是加载时间超过1250ms的类,因而需要在zygote预加载
2、建立IPC通信服务——初始化ZygoteServer,内部初始化了ZygoteSocket
zygote及系统中其他程序的通信并没有使用Binder,而是采用基于AF_UNIX类型的Socket,作用正是建立这个Socket
3、启动system_server——forkSystemServer()
这个函数会创建Java世界中系统Service所驻留的进程system_server,该进程是framework的核心,也是zygote孵化出的第一个进程。如果它死了,就会导致zygote自杀。
4、等待请求——runSelectLoop()
zygote从startSystemServer返回后,将进入第四个关键函数runSelectLoop,在第一个函数ZygoteServer中注册了一个用于IPC的Socket将在这里使用,这里Zygote采用高效的I/O多路复用机制,保证在没有客户端请求时或者数据处理时休眠,否则响应客户端的请求。等待 AMS 给他发消息(告诉 zygote 创建进程)。此时zygote完成了java世界的初创工作,调用runSelectLoop便开始休眠了,当收到请求或者数据处理便会随时醒来,继续工作。

4 面试题

1 init进程作用是什么

init进程起着承上启下的作用,Android本身是基于Linux而来的,init进程是Linux系统中用户空间的第一个进程。init进程属于一个守护进程,准确的说,它是Linux系统中用户控制的第一个进程,它的进程号为1(进程号为0的为内核进程),它的生命周期贯穿整个Linux内核运行的始终。Android中所有其它的进程共同的鼻祖均为init进程。
Android Q(10.0) 的init入口函数由原先的init.cpp 调整到了main.cpp,把各个阶段的操作分离开来,使代码更加简洁命令。
作为天子第1号进程,init被赋予了很多重要的职责,主要分为三个阶段:

  1. init进程第一阶段做的主要工作是挂载分区,创建设备节点和一些关键目录,初始化日志输出系统,启用SELinux安全策略。
  2. init进程第二阶段主要工作是初始化属性系统,解析SELinux的匹配规则,处理子进程终止信号,启动系统属性服务,可以说每一项都很关键,如果说第一阶段是为属性系统,SELinux做准备,那么第二阶段就是真正去把这些功能落实。
  3. init进行第三阶段主要是解析init.rc来启动其他进程,进入无限循环,进行子进程实时监控(守护)

其中第三阶段通过initrc启动其他进程,我们常见的比如启动Zygote进程启动SeviceManager进程等。

2 Zygote进程最原始的进程是什么进程(或者Zygote进程由来)

Zygote最开始是app_process,它是在 init 进程启动时被启动的,在app_main.cpp才被修改为 Zygote。

3 Zygote 是在内核空间还是在用户空间?

因为 init 进程的创建在用户空间,而 Zygote 是由 init 进程创建启动的,所以Zygote是在用户空间。

4 Zygote为什么需要用到Socket通信而不是Binder

Zygote是Android中的一个重要进程,它是启动应用程序进程的父进程。Zygote使用Socket来与应用程序进程进行通信,而不是使用Android中的IPC机制Binder,这是因为Socket和Binder有不同的优缺点,而在Zygote进程中使用Socket可以更好地满足Zygote进程的需求。

  1. Zygote 用 binder 通信会导致死锁
    假设 Zygote 使用 Binder 通信,因为 Binder 是支持多线程的,存在并发问题,而并发问题的解决方案就是加锁,如果进程 fork 是在多线程情况下运行,Binder 等待锁在锁机制下就可能会出现死锁。
  2. Zygote 用 binder 通信会导致读写错误
    根本原因在于要 new 一个 ProcessState 用于 Binder 通信时,需要 mmap 申请一片内存用以提供给内核进行数据交换使用。而如果直接 fork 了的话,子进程在进行 binder 通信时,内核还是会继续使用父进程申请的地址写数据,而此时会触发子进程 COW(Copy on Write),从而导致地址空间已经重新映射,而子进程还尝试访问之前父进程 mmap 的地址,会导致 SIGSEGV、SEGV_MAPERR段错误。
  3. Zygote初始化时,Binder还没开始初始化。
  4. Socket具有良好的跨平台性,能够在不同的操作系统和语言之间进行通信。这对于Zygote进程来说非常重要,因为它需要在不同的设备和架构上运行,并且需要与不同的应用程序进程进行通信。使用Socket可以让Zygote进程更加灵活和可扩展,因为它不需要考虑Binder所带来的特定限制和要求。
  5. Socket具有简单的API和易于使用的特点。Zygote进程需要快速启动并与应用程序进程建立通信,Socket提供了快速、可靠的通信方式,并且使用Socket API也很容易实现。相比之下,Binder需要更多的配置和维护工作,这对于Zygote进程来说可能会增加不必要的复杂性和开销。
  6. Socket在数据传输时具有更低的延迟和更高的吞吐量,这对于Zygote进程来说非常重要。Zygote进程需要在较短的时间内启动应用程序进程,并且需要传输大量的数据和代码,Socket的高性能和低延迟使其成为更好的选择。

总之,Zygote进程使用Socket而不是Binder是基于其优点和需求而做出的选择。虽然Binder在Android中扮演着重要的角色,但在某些情况下,使用Socket可以提供更好的性能和更大的灵活性。再者,Binder当初并不成熟,团队成员对于进程间通讯更倾向于用Socket,后面为了做了很多优化,才使得Binder通讯变得成熟稳定。

5 每个App都会将系统的资源,系统的类都加载一遍吗

zygote进程的作用:
1.创建一个Service端的Socket,开启一个ServerSocket实现和别的进程通信。
2.加载系统类,系统资源。
3.启动System Server进程

Zygote进程预加载系统资源后,然后通过它孵化出其他的虚拟机进程,进而共享虚拟机内存和框架层资源(共享内存),这样大幅度提高应用程序的启动和运行速度。

6 PMS 是干什么的,你是怎么理解PMS

包管理,包解析,结果缓存,提供查询接口。

  1. 遍历/data/app的文件夹
  2. 解压apk文件
  3. dom解析AndroidManifest.xml文件。

7 为什么会有AMS AMS的作用

  1. 查询PMS
  2. 反射生成对象
  3. 管理Activity生命周期

AMS缓存中心:ActivityThread

8 AMS如何管理Activity,探探AMS的执行原理

Activity在应用端由ActivityClientRecord负责描述其生命周期的过程与状态,但最终这些过程与状态是由ActivityManagerService(以下简称AMS)来管理和控制的

  1. BroadcastRecord:描述了应用进程的BroadcastReceiver,由BroadcastQueue负责管理。
  2. ServiceRecord:描述了Service服务组件,由ActiveServices负责管理。
  3. ContentProviderRecord:描述ContentProvider内容提供者,由ProviderMap管理。
  4. ActivityRecord:用于描述Activity,由ActivityStackSupervisor进行管理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/32736.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C#串口通信从入门到精通(26)——多个串口多个线程发送数据和接收数据

前言 我们在开发串口程序的过程中有时候会遇到多个串口,并且多个串口也需要在多个线程进行操作,本文就来讲解如何实现多个串口在多线程下的安全发送与接收。 1、操作界面与测试过程 我们首先使用虚拟串口助手虚拟COM1、COM2这一对串口;COM3、COM4这一对串口,然后使用代码…

Redis 原理

Redis 原理 动态字符串SDS Redis中保存的key时字符串&#xff0c;value往往是字符串或字符串集合&#xff0c;字符串是Redis中常见的数据结构 Redis没有直接使用C语言中的字符串&#xff0c;因为C语言字符串存在很多问题&#xff0c;使用起来不方便 Redis构建了一种新型的字…

TypeScript 【类型推断】与【类型别名】的使用解读

什么是类型推断&#xff1f; 在 TypeScript 中&#xff0c; 如果声明变量时&#xff0c;没有明确的指定类型&#xff0c;那么 TypeScript 会依照类型推论&#xff08;Type Inference&#xff09;的规则推断出一个类型。 以下代码虽然没有明确指定类型&#xff0c;但是会在编译的…

应急响应:系统入侵排查指南

目录 系统基本信息排查 Windows系统排查 Linux系统排查 CPU信息 操作系统信息 载入模块排查 用户排查 Windows系统用户排查 排查所有账户 Linux用户排查 root账户排查 查看所有可登录账户 查看用户错误的登录信息 查看所有用户最后登录信息 排查空口令账户 启…

【Flutter】如何在 Flutter 中获取设备 ID

文章目录 一、 前言二、 设备 ID 的重要性1. 什么是设备 ID2. 设备 ID 的作用 三、 在 Flutter 中获取设备 ID1. 需要的工具和库2. 简单代码示例3. 完整可以运行的代码 四、 注意事项1. 权限问题2. 设备兼容性问题 五、 总结 一、 前言 在移动应用开发中&#xff0c;有时我们需…

面试官: 请你讲下AMS在Android起到什么作用……

心理分析&#xff1a;这道题在发生在大多数场景下。面对这道题 很多求职很茫然&#xff0c;不知道该如何说起。AMS本身比较复杂难以理解。工作多年也很难弄清AMS的作用&#xff0c;其实我们大可从以下几点入手组件启动、进程切换、Crash异常入手 求职者:AMS难以表述 我们就从最…

【机器学习】PCA案例的python实现

一、说明 虽然可以通过更改优化算法来加快机器学习算法的拟合速度&#xff0c;但加快算法速度的更常用方法是使用主成分分析 &#xff08;PCA&#xff09;。如果您的学习算法由于输入维度太高而太慢&#xff0c;那么使用 PCA 加速它可能是一个合理的选择。这可能是PCA最常见的应…

正则表达式-捕获组,命名捕获组,非捕获组

正则表达式的作用 测试目标字符串是否符合规则 返回true/false按照规则从目标字符串提取内容 返回匹配的数组 在线测试工具 regex101: build, test, and debug regexRegular expression tester with syntax highlighting, explanation, cheat sheet for PHP/PCRE, Python, …

【单片机】STM32F103C8T6 最小系统板原理图

STM32F103C8T6是一款基于ARM Cortex-M3内核的32位微控制器&#xff0c;由STMicroelectronics&#xff08;ST&#xff09;公司生产。它是STMicroelectronics的STM32系列微控制器中的一员&#xff0c;被广泛应用于嵌入式系统和电子设备中。 STM32F103C8T6单片机的主要特点和资源…

基于.Net6使用YoloV8的分割模型

前言 在目标检测一文中&#xff0c;我们学习了如何处理Onnx模型&#xff0c;并的到目标检测结果&#xff0c;在此基础上&#xff0c;本文实现基于.Net平台的实例分割任务。 执行YoloV8的分割任务后可以得到分割.pt模型。由于Python基本不用于工业软件的部署&#xff0c;最终还…

SpringBoot 如何使用 @RequestBody 进行数据校验

SpringBoot 如何使用 RequestBody 进行数据校验 在 Web 开发中&#xff0c;前台向后台发送数据是非常常见的场景。而在 SpringBoot 框架中&#xff0c;我们通常使用 RequestBody 注解来接收前台发送的 JSON 数据&#xff0c;并将其转化为 Java 对象。但是&#xff0c;接收到的…

Zookeeper集群的特点

一、Zookeeper集群的特点 Zookeeper:一个领导者 (Leader)&#xff0c;多个跟随者 (Follower) 组成的集群集群中只要有半数以上节点存活&#xff0c;Zookeeper集群就能正常服务。所以Zookeeper适合安装奇数台服务器全局数据一致:每个Server保存一份相同的数据副本&#xff0c;C…

git——使用ssh连接远程仓库

文章目录 前言一. 获取邮箱和密码1. 本地配置你的名字和邮箱2. 使用命令获取你本地的邮箱和密码 二、生成ssh公钥1.任意一个文件夹路径打开Git Bash Here并输入以下命令连按三次回车2. 根据上面红框部分的地址打开文件夹3. 打开并查看id_rsa.pub 文件 三、在GitHub上连接ssh1. …

UE5.1.1 C++从0开始(15.作业4个人作业分享)

教程链接&#xff1a;https://www.bilibili.com/video/BV1nU4y1X7iQ 好吧这个作业应该是之前写的&#xff0c;但是我发现我没写&#xff0c;后面我又回去自己写了一遍再看代码&#xff0c;感觉上大差不差&#xff0c;各位可以看着我的和老师的还有自己的对比下。 SBTService_…

[LeetCode周赛复盘] 第 107 场双周赛20230624

[LeetCode周赛复盘] 第 107 场双周赛20230624 一、本周周赛总结6898. 字符串连接删减字母1. 题目描述2. 思路分析3. 代码实现 6895. 构造最长的新字符串1. 题目描述2. 思路分析3. 代码实现 6898. 字符串连接删减字母1. 题目描述2. 思路分析3. 代码实现 6468. 统计没有收到请求…

Consul 理解

Consul是google开源的一个使用go语言开发的服务发现、配置管理中心服务。内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案&#xff0c;不再需要依赖其他工具&#xff08;比如ZooKeeper等&#xff09;。服务部署简单&#xff0c;只有一…

(转载)支持向量机(support vector machine, SVM)的分类(matlab实现)

支持向量机(support vector machine,SVM)是一种新的机器学习方法&#xff0c;其基础是Vapnik 创建的统计学习理论(statistical learning theory,STL)。统计学习理论采用结构风险最小化(structural risk minimization,SRM)准则&#xff0c;在最小化样本点误差的同时&#xff0c;…

HTML点击显示、点击隐藏details 标签

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>菜鸟教程(runoob.com)</title> </head> <body><details> <summary>Copyright 1999-2011.</summary> <p> - by Refsnes Da…

Redis数据类型

Redis数据类型 一、String数据类型1、概述2、实验 二、List数据类型1、概述2、实验 三、Hash数据类型&#xff08;散列类型&#xff09;1、概述2、实验 四、Set数据类型&#xff08;无序集合&#xff09;1、概述2、应用范围3、实验 五、Sorted Set数据类型&#xff08;zset、有…

JAVA开发与运维(怎么通过docker部署微服务jar包)

目标&#xff1a; 通过docker的方式部署微服务。 一、背景&#xff1a; 我们通过java开发的微服务可以打成jar包&#xff0c;我们可以直接通过裸机部署&#xff0c;也可以通过docker来部署&#xff0c;本文介绍通过docker来部署微服务。 二、首先我们介绍一下docker的发展过程…
最新文章