Zygote的启动流程

在zygote进程对应的文件是app_main.cpp文件,在app_main.cpp文件的main()方法中先解析了init.rc中配置的参数并根据配置的参数设置zygote的状态。

在状态设置阶段主要做了:

  • 设置进程名称为zygote
  • 通过startSystemServer = true标示启动的是systemServer
  • 调用runtime.start()方法
............
 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      }

然后调用runtime.start()方法。runtime的类型是由AndroidRuntime派生类AppRunTime,start()方法定义在AndroidRunTime类中。在启动zygote进程是start()方法接受的第一参数是com.android.internal.os.ZygoteInit,这个参数很重要。下面看一下AndroidRuntime——>start()方法的实现:

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
1137  {
1138      ALOGD(">>>>>> START %s uid %d <<<<<<\n",
1139              className != NULL ? className : "(unknown)", getuid());
1140  
1141      static const String8 startSystemServer("start-system-server");
1142      // Whether this is the primary zygote, meaning the zygote which will fork system server.
1143      bool primary_zygote = false;
1144  
1145      /*
1146       * 'startSystemServer == true' means runtime is obsolete and not run from
1147       * init.rc anymore, so we print out the boot start event here.
1148       */
1149      for (size_t i = 0; i < options.size(); ++i) {
1150          if (options[i] == startSystemServer) {
1151              primary_zygote = true;
1152             /* track our progress through the boot sequence */
1153             const int LOG_BOOT_PROGRESS_START = 3000;
1154             LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
1155          }
1156      }
1157  
1158      const char* rootDir = getenv("ANDROID_ROOT");
1159      if (rootDir == NULL) {
1160          rootDir = "/system";
1161          if (!hasDir("/system")) {
1162              LOG_FATAL("No root directory specified, and /system does not exist.");
1163              return;
1164          }
1165          setenv("ANDROID_ROOT", rootDir, 1);
1166      }
1167  
1168      const char* artRootDir = getenv("ANDROID_ART_ROOT");
1169      if (artRootDir == NULL) {
1170          LOG_FATAL("No ART directory specified with ANDROID_ART_ROOT environment variable.");
1171          return;
1172      }
1173  
1174      const char* i18nRootDir = getenv("ANDROID_I18N_ROOT");
1175      if (i18nRootDir == NULL) {
1176          LOG_FATAL("No runtime directory specified with ANDROID_I18N_ROOT environment variable.");
1177          return;
1178      }
1179  
1180      const char* tzdataRootDir = getenv("ANDROID_TZDATA_ROOT");
1181      if (tzdataRootDir == NULL) {
1182          LOG_FATAL("No tz data directory specified with ANDROID_TZDATA_ROOT environment variable.");
1183          return;
1184      }
1185  
1186      //const char* kernelHack = getenv("LD_ASSUME_KERNEL");
1187      //ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
1188  
1189      /* start the virtual machine */
1190      JniInvocation jni_invocation;
1191      jni_invocation.Init(NULL);
1192      JNIEnv* env;
1193      if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
1194          return;
1195      }
1196      onVmCreated(env);
1197  
1198      /*
1199       * Register android functions.
1200       */
1201      if (startReg(env) < 0) {
1202          ALOGE("Unable to register all android natives\n");
1203          return;
1204      }
1205  
1206      /*
1207       * We want to call main() with a String array with arguments in it.
1208       * At present we have two arguments, the class name and an option string.
1209       * Create an array to hold them.
1210       */
1211      jclass stringClass;
1212      jobjectArray strArray;
1213      jstring classNameStr;
1214  
1215      stringClass = env->FindClass("java/lang/String");
1216      assert(stringClass != NULL);
1217      strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
1218      assert(strArray != NULL);
1219      classNameStr = env->NewStringUTF(className);
1220      assert(classNameStr != NULL);
1221      env->SetObjectArrayElement(strArray, 0, classNameStr);
1222  
1223      for (size_t i = 0; i < options.size(); ++i) {
1224          jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
1225          assert(optionsStr != NULL);
1226          env->SetObjectArrayElement(strArray, i + 1, optionsStr);
1227      }
1228  
1229      /*
1230       * Start VM.  This thread becomes the main thread of the VM, and will
1231       * not return until the VM exits.
1232       */
1233      char* slashClassName = toSlashClassName(className != NULL ? className : "");
1234      jclass startClass = env->FindClass(slashClassName);
1235      if (startClass == NULL) {
1236          ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
1237          /* keep going */
1238      } else {
1239          jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
1240              "([Ljava/lang/String;)V");
1241          if (startMeth == NULL) {
1242              ALOGE("JavaVM unable to find main() in '%s'\n", className);
1243              /* keep going */
1244          } else {
1245              env->CallStaticVoidMethod(startClass, startMeth, strArray);
1246  
1247  #if 0
1248              if (env->ExceptionCheck())
1249                  threadExitUncaughtException(env);
1250  #endif
1251          }
1252      }
1253      free(slashClassName);
1254  
1255      ALOGD("Shutting down VM\n");
1256      if (mJavaVM->DetachCurrentThread() != JNI_OK)
1257          ALOGW("Warning: unable to detach main thread\n");
1258      if (mJavaVM->DestroyJavaVM() != 0)
1259          ALOGW("Warning: VM did not shut down cleanly\n");
1260  }

AndroidRuntime主要做了三件事儿:

  • 调用startVM()函数启动虚拟机
  • 调用startReg()函数注册jni方法
  • 通过调用的方法调用com.android.internal.os.ZygoteInit类main()方法。

startVM()方法多了大量的命令参数拼接最后通过JNI_CreateJavaVM()方法创建虚拟机。

startReg()方法注册JNI方法,Android中java和Native层的交互需要通过JNI机制来完成,并且Android中大量使用JNI函数。

看下面截图方法:

其中CallStaticVoidMethod 这个方法就是调用ZygoteInit这个类中的main方法,终于到java中了。

 public static void main(String argv[]) {
833          ZygoteServer zygoteServer = null;
834  
835          // Mark zygote start. This ensures that thread creation will throw
836          // an error.
837          ZygoteHooks.startZygoteNoThreadCreation();
838  
839          // Zygote goes into its own process group.
840          try {
841              Os.setpgid(0, 0);
842          } catch (ErrnoException ex) {
843              throw new RuntimeException("Failed to setpgid(0,0)", ex);
844          }
845  
846          Runnable caller;
847          try {
848              // Store now for StatsLogging later.
849              final long startTime = SystemClock.elapsedRealtime();
850              final boolean isRuntimeRestarted = "1".equals(
851                      SystemProperties.get("sys.boot_completed"));
852  
853              String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";
854              TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
855                      Trace.TRACE_TAG_DALVIK);
856              bootTimingsTraceLog.traceBegin("ZygoteInit");
857              RuntimeInit.preForkInit();
858  
859              boolean startSystemServer = false;
860              String zygoteSocketName = "zygote";
861              String abiList = null;
862              boolean enableLazyPreload = false;
863              for (int i = 1; i < argv.length; i++) {
864                  if ("start-system-server".equals(argv[i])) {
865                      startSystemServer = true;
866                  } else if ("--enable-lazy-preload".equals(argv[i])) {
867                      enableLazyPreload = true;
868                  } else if (argv[i].startsWith(ABI_LIST_ARG)) {
869                      abiList = argv[i].substring(ABI_LIST_ARG.length());
870                  } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
871                      zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
872                  } else {
873                      throw new RuntimeException("Unknown command line argument: " + argv[i]);
874                  }
875              }
876  
877              final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);
878              if (!isRuntimeRestarted) {
879                  if (isPrimaryZygote) {
880                      FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
881                              BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__ZYGOTE_INIT_START,
882                              startTime);
883                  } else if (zygoteSocketName.equals(Zygote.SECONDARY_SOCKET_NAME)) {
884                      FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
885                              BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SECONDARY_ZYGOTE_INIT_START,
886                              startTime);
887                  }
888              }
889  
890              if (abiList == null) {
891                  throw new RuntimeException("No ABI list supplied.");
892              }
893  
894              // In some configurations, we avoid preloading resources and classes eagerly.
895              // In such cases, we will preload things prior to our first fork.
896              if (!enableLazyPreload) {
897                  bootTimingsTraceLog.traceBegin("ZygotePreload");
898                  EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
899                          SystemClock.uptimeMillis());
900                  preload(bootTimingsTraceLog);
901                  EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
902                          SystemClock.uptimeMillis());
903                  bootTimingsTraceLog.traceEnd(); // ZygotePreload
904              }
905  
906              // Do an initial gc to clean up after startup
907              bootTimingsTraceLog.traceBegin("PostZygoteInitGC");
908              gcAndFinalize();
909              bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC
910  
911              bootTimingsTraceLog.traceEnd(); // ZygoteInit
912  
913              Zygote.initNativeState(isPrimaryZygote);
914  
915              ZygoteHooks.stopZygoteNoThreadCreation();
916  
917              zygoteServer = new ZygoteServer(isPrimaryZygote);
918  
919              if (startSystemServer) {
920                  Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
921  
922                  // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
923                  // child (system_server) process.
924                  if (r != null) {
925                      r.run();
926                      return;
927                  }
928              }
929  
930              Log.i(TAG, "Accepting command socket connections");
931  
932              // The select loop returns early in the child process after a fork and
933              // loops forever in the zygote.
934              caller = zygoteServer.runSelectLoop(abiList);
935          } catch (Throwable ex) {
936              Log.e(TAG, "System zygote died with exception", ex);
937              throw ex;
938          } finally {
939              if (zygoteServer != null) {
940                  zygoteServer.closeServerSocket();
941              }
942          }
943  
944          // We're in the child process and have exited the select loop. Proceed to execute the
945          // command.
946          if (caller != null) {
947              caller.run();
948          }
949      }

ZygoteInit的main()方法主要做了5件事儿:

通过registerZygoteSocket()函数创建了一个Zygote的socket接口用来和AMS进行通信。
调用preload()预加载类和资源
调用startSystemServer()方法启动systemServer
调用runSelectLoop()方法监听socket接口
通过捕获一个MethodAndArgsCaller异常并调用捕获的异常的run()方法。

1.预加载 

其中画红线的部分是拷贝到我们的应用进程中的。代码如下:

preload()方法主要预先加载了framework中通用的类和资源(core/res/res/values/arrays.xml)、openGL、本地共享库、webView的一些资源。 

预先加载主要是为了在启动新应用程序时不用重复加载这些资源从而加快启动速度。

2.创建Zygote Socket 服务 

zygoteServer = new ZygoteServer(isPrimaryZygote);这里没有用Binder机制,

(1).binder 没有初始化完成

(2)fork写时拷贝,容易死锁

zygote的socket接口主要是通过/dev/socket/zygote文件的文件操作符进行通信。

3. 然后创建 system_server进程

最终调用的事Zygote类里面的forkSystemServer方法 

4. runSelectLoop,等待AMS发送消息,创建进程

 

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

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

相关文章

6.s081 学习实验记录(三)system calls

文章目录 一、use gdb二、syscall&#xff1a;trace注意&#xff1a;实验代码&#xff1a;实验结果&#xff1a; 三、sysinfotips&#xff1a;实验代码实验结果 需要切换到 syscall 分支 一、use gdb 学习使用 gdb 调试 make qemu-gdb打开一个新的终端&#xff1a; gdb-mult…

换个思路快速上手UML和plantUML——时序图

上一章我们介绍了类图&#xff0c;我们很清楚&#xff0c;类图是从更加宏观的角度去梳理系统结构的&#xff0c;从类图中我们可以获取到类与类之间&#xff1a;继承&#xff0c;实现等关系信息&#xff0c;是宏观逻辑。下面我们继续换一个思路&#xff1a;作为一名软件工程结构…

【周赛】第382场周赛

&#x1f525;博客主页&#xff1a; A_SHOWY&#x1f3a5;系列专栏&#xff1a;力扣刷题总结录 数据结构 云计算 数字图像处理 力扣每日一题_ 从这一场&#xff08;第382场周赛&#xff09;周赛开始记录&#xff0c;目标是尽快达到准确快速AC前三道题&#xff0c;每场比赛…

贝莱德里程碑

作者&#xff1a;秦晋 见证奇迹的时刻。 1月27日&#xff0c;全球资产管理巨头贝莱德在美国证监会于1月10日审批通过比特币ETF的17天内&#xff0c;其比特币现货ETF资产管理规模已经突破20亿美元。持有比特币总数已达49952个。领跑其他9只比特币ETF机构参与者。不包括灰度GBTC&…

【JavaScript 漫游】专栏介绍

专栏介绍 本专栏旨在记录 JavaScript 核心语法&#xff0c;作为笔者日常学习和工作中的参考手册和代码示例仓库。 内容上力求覆盖 ES5、DOM、BOM 和 ES6 规范的所有内容。对于常用且重要的知识点&#xff0c;应该详细描述并附带有大量的代码示例。对于在工作场景中很少用到的…

数据结构——用Java实现二分搜索树

目录 一、树 二、二分搜索树 1.二叉树 2.二分搜索树 三、代码实现 1.树的构建 2.获取树中结点的个数 3.添加元素 4.查找元素 &#xff08;1&#xff09;查找元素是否存在 &#xff08;2&#xff09;查找最小元素 &#xff08;3&#xff09;查找最大元素 5.二分搜索…

算法39:统计全 1 子矩形(力扣1504)----单调栈

题目: 给你一个 m x n 的二进制矩阵 mat &#xff0c;请你返回有多少个 子矩形 的元素全部都是 1 。 示例 1&#xff1a; 输入&#xff1a;mat [[1,0,1],[1,1,0],[1,1,0]] 输出&#xff1a;13 解释&#xff1a; 有 6 个 1x1 的矩形。 有 2 个 1x2 的矩形。 有 3 个 2x1 的矩…

(2024|ICLR,MAD,真实数据与合成数据,自吞噬循环)自消耗生成模型变得疯狂

Self-Consuming Generative Models Go MAD 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 2. 自吞噬生成模型 2.1 自吞噬过程 2.2 自吞噬过程的变体 2.3 自吞噬循环中的偏…

Advanced EFS Data Recovery:恢复 Windows NTFS 中 EFS 加密文件

Advanced EFS Data Recovery 数据恢复软件可以破解 NTFS 加密&#xff0c;并解密受 Windows 加密文件系统 &#xff08;EFS&#xff09; 保护的文件。 Advanced EFS Data Recovery 功能列表 通过破解加密文件系统 &#xff08;EFS&#xff09; 来解除 NTFS 加密 解密转移到另…

Java面试题(11)

59.说一说springMVC的运行流程 1. 用户向服务器发送请求&#xff0c;请求被 Spring 前端控制 Servelt DispatcherServlet 捕获&#xff1b; 2. DispatcherServlet 对请求 URL 进行解析&#xff0c;得到请求资源标识符&#xff08;URI&#xff09;。然后根据该 URI&#xff0c;…

STM32 E18-D80NK红外避障传感器

E18-D80NK-N是一款红外光电传感器&#xff0c;它同时具备发射和接收功能。通过对发射光进行调制后发出&#xff0c;并通过接收头对反射光进行解调输出。 E18-D80NK-N采用了透镜来增强传感器的性能&#xff0c;使其能够检测更远的距离。根据红外光的特性&#xff0c;不同颜色的…

VitePress-04-文档中的表情符号的使用

说明 vitepress 的文档中是支持使用表情符号的&#xff0c;像 &#x1f602; 等常用的表情都是支持的。 本文就来介绍它的使用方式。 使用语法 语法 &#xff1a; :表情名称: 例如 &#xff1a; :joy: &#x1f602; 使用案例代码 # 体会【表情】的基本使用 > hello world …

22.Lambda 表达式

Lambda 表达式 1. 概况2. 函数式接口3. 格式3.1 完整格式3.2 省略格式 4. 代码示例5. 输出结果6. 注意事项 学习Lambda表达式之前最好先学会 匿名内部类 具体信息请查看 API 帮助文档 1. 概况 Lambda 表达式是一种在编程中用来表示匿名函数的简洁语法。它是基于函数式编程风格…

2024.1.27每日一题

LeetCode 最大合金数 2861. 最大合金数 - 力扣&#xff08;LeetCode&#xff09; 题目描述 假设你是一家合金制造公司的老板&#xff0c;你的公司使用多种金属来制造合金。现在共有 n 种不同类型的金属可以使用&#xff0c;并且你可以使用 k 台机器来制造合金。每台机器都需…

SpringBoot之JWT登录

JWT JSON Web Token&#xff08;JSON Web令牌&#xff09; 是一个开放标准(rfc7519)&#xff0c;它定义了一种紧凑的、自包含的方式&#xff0c;用于在各方之间以JSON对象安全地传输信息。此信息可以验证和信任&#xff0c;因为它是数字签名的。jwt可以使用秘密〈使用HNAC算法…

嵌入式学习第十三天

9.指针: &#xff08;1&#xff09;const指针 const 关键字 常量(只读) 1.const int *p; 2.int const *p; 1和2是等价的 const修饰 *p,指针变量p的值可以改变,但不能利用指针修改指向空间中的值 3.int *const p; const修饰 p,指针变量p的值不能改变…

【教程】极简Docker搭建“帕鲁幻兽PalWorld”服务器, 附资源

注意&#xff1a; 如果搭建在个人服务器或者内网中&#xff0c;需要做内网穿透&#xff0c;可以看这篇博客&#xff1a; 【教程】超详细安装和使用免费内网穿透软件Zerotier-One-CSDN博客文章浏览阅读523次&#xff0c;点赞8次&#xff0c;收藏8次。真的很详细https://blog.csd…

[设计模式Java实现附plantuml源码~结构型]树形结构的处理——组合模式

前言&#xff1a; 为什么之前写过Golang 版的设计模式&#xff0c;还在重新写Java 版&#xff1f; 答&#xff1a;因为对于我而言&#xff0c;当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言&#xff0c;更适合用于学习设计模式。 为什么类图要附上uml 因为很…

Mysql查询数据

1 基本查询语句 MySQL从数据表中查询数据的基本语句为SELECT语句。SELECT语句的基本格式是&#xff1a; 2 单表查询 2.1 查询所有字段 SELECT * FROM 表名; 2.2 在SELECT语句中指定所有字段 SELECT f_id, s_id ,f_name, f_price FROM fruits; 2.3 查询单个字段 SELECT 列名FR…

Ajax入门与使用

目录 ◆ AJAX 概念和 axios 使用 什么是 AJAX&#xff1f; 怎么发送 AJAX 请求&#xff1f; 如何使用axios axios 函数的基本结构 axios 函数的使用场景 1 没有参数的情况 2 使用params参数传参的情况 3 使用data参数来处理请求体的数据 4 上传图片等二进制的情况…