作者:贾东风

1. zygote是什么?

在 Android 系统中,JavaVM(Java 虚拟机)应用程序进程以及运行系统关键服务的 SystemServer 进程都是由 Zygote 来创建的,我们也将它称为 孵化器。它通过 fock (复制进程)的形式来创建 "应用程序进程""SystemServer 进程",由于 Zygote 进程在启动时会创建 JavaVM,因此通过 fock 而创建的 “应用程序进程” 和 “SystemServer 进程” 可以在内部获取一个 JavaVM 的实例拷贝

2. zygote启动脚本

zygote的rc脚本是包含在system/core/rootdir/init.rc中的

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc    // ${ro.zygote} 由厂商定义,与平台相关

init.zygote32.rc init.zygote64.rc 只会启动一个朱进程 init.zygote32_64.rc init.zygote64_32.rc 会分别启动主副zygote进程

init.zygote32_64.rc:启动两个 zygote 进程 (zygote 和 zygote_secondary),对应的执行程序分别是 app_process32 (主模式)、app_process64

//init.zygote64_32.rc
service zygote /system/bin/app_process64
-Xzygote /system/bin
--zygote
--start-system-server
--socket-name=zygoteservice zygote_secondary /system/bin/app_process32
-Xzygote /system/bin
--zygote
--socket-name=zygote_secondary
--enable-lazy-preload

2.1 主要流程:

  1. 在init中通过init进程解析zytoe.rc中的配置执行脚本,通过FindService方法找到rc中配置的zygote服务, fork出zygote进程
  2. 在fork出的zygote进程中,执行service对应的/system/bin/app_process64脚本,进入app_main.cpp对应的main方法

详情参考: segmentfault.com/a/119000002…

3. app_main#main

int main(int argc, char* const argv[])
{
...AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
...// Parse runtime arguments.  Stop at first unrecognized option.bool zygote = false;//处于zygote进程中bool startSystemServer = false;bool application = false;//处于application进程String8 niceName;String8 className;//1\. 解析参数++i;  // Skip unused "parent dir" argument.while (i < argc) {const char* arg = argv[i++];if (strcmp(arg, "--zygote") == 0) {zygote = true;niceName = ZYGOTE_NICE_NAME;} else if (strcmp(arg, "--start-system-server") == 0) {startSystemServer = true;} else if (strcmp(arg, "--application") == 0) {application = true;} else if (strncmp(arg, "--nice-name=", 12) == 0) {niceName.setTo(arg + 12);} else if (strncmp(arg, "--", 2) != 0) {className.setTo(arg);break;} else {--i;break;}}//2\. 准备参数Vector<String8> args;if (!className.isEmpty()) {if (!LOG_NDEBUG) {String8 restOfArgs;char* const* argv_new = argv + i;int argc_new = argc - i;for (int k = 0; k < argc_new; ++k) {restOfArgs.append("\"");restOfArgs.append(argv_new[k]);restOfArgs.append("\" ");}ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());}} else {//处于zytote进程模式// We're in zygote mode.maybeCreateDalvikCache();if (startSystemServer) {args.add(String8("start-system-server"));}...String8 abiFlag("--abi-list=");abiFlag.append(prop);args.add(abiFlag);// In zygote mode, pass all remaining arguments to the zygote// main() method.for (; i < argc; ++i) {args.add(String8(argv[i]));}}if (!niceName.isEmpty()) {runtime.setArgv0(niceName.string(), true /* setProcName */);}//3\. 根据所处进程的不同,执行不同的初始化if (zygote) {runtime.start("com.android.internal.os.ZygoteInit", args, zygote);} else if (className) {runtime.start("com.android.internal.os.RuntimeInit", args, zygote);} else {fprintf(stderr, "Error: no class name or --zygote supplied.\n");app_usage();LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");}
}

3.1 主要流程:

  1. 解析main方法的调用参数,判断当前所处的进程模式
  2. 如果是处于zygote进程,则完成zygote的初始化,并创建systemserver进程
  3. 如果是处于应用进程,则完成运行时初始化

4. ZygoteInit初始化流程

4.1 AndroidRuntime.start

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{...//启动虚拟机JniInvocation jni_invocation;jni_invocation.Init(NULL);JNIEnv* env;if (startVm(&mJavaVM, &env, zygote) != 0) {return;}onVmCreated(env);/** 2、Java虚拟机注册JNI方法*/if (startReg(env) < 0) {ALOGE("Unable to register all android natives\n");return;}...// 3classNameStr = env->NewStringUTF(className);assert(classNameStr != NULL);env->SetObjectArrayElement(strArray, 0, classNameStr);for (size_t i = 0; i < options.size(); ++i) {jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());assert(optionsStr != NULL);env->SetObjectArrayElement(strArray, i + 1, optionsStr);}/** Start VM.  This thread becomes the main thread of the VM, and will* not return until the VM exits.*/// 4char* slashClassName = toSlashClassName(className != NULL ? className : "");jclass startClass = env->FindClass(slashClassName);if (startClass == NULL) {ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);/* keep going */} else {// 6jmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");if (startMeth == NULL) {ALOGE("JavaVM unable to find main() in '%s'\n", className);/* keep going */} else {// 6env->CallStaticVoidMethod(startClass, startMeth, strArray);#if 0if (env->ExceptionCheck())threadExitUncaughtException(env);
#endif}}free(slashClassName);...
}

在main方法中通过runtime.start方法,启动虚拟机,然后通过JNI的方式,调用java 层的ZygoteInit的main方法

首先,在AndroidRuntime的start函数中,主要处理流程如下:

  1. 使用startVm函数来启动弄Java虚拟机,
  2. 使用startReg函数为Java虚拟机注册JNI方法。

解析com.android.internal.os.ZygoteInit字符串,通过jni调用该类的main方法,从native进入java世界

4.2 ZygoteInit.main

4.2.1 preload

  1. preloadClasses

PRELOADED_CLASSES = “/system/etc/preloaded-classes”;

使用class.forname在虚拟机中中预加载preloaded-classes

aosp:/ # cat /system/etc/preloaded-classes
un.util.logging.LoggingProxy
sun.util.logging.LoggingSupport
sun.util.logging.LoggingSupport$1
sun.util.logging.PlatformLogger
sun.util.logging.PlatformLogger$1
sun.util.logging.PlatformLogger$Level
....
  1. preloadResources
    private static void preloadResources() {final VMRuntime runtime = VMRuntime.getRuntime();try {mResources = Resources.getSystem();mResources.startPreloading();if (PRELOAD_RESOURCES) {...TypedArray ar = mResources.obtainTypedArray(com.android.internal.R.array.preloaded_drawables);int N = preloadDrawables(ar);...ar = mResources.obtainTypedArray(com.android.internal.R.array.preloaded_color_state_lists);N = preloadColorStateLists(ar);...if (mResources.getBoolean(com.android.internal.R.bool.config_freeformWindowManagement)) {startTime = SystemClock.uptimeMillis();ar = mResources.obtainTypedArray(com.android.internal.R.array.preloaded_freeform_multi_window_drawables);N = preloadDrawables(ar);...}}mResources.finishPreloading();} catch (RuntimeException e) {Log.w(TAG, "Failure preloading resources", e);}}

分别调用preloadDrawables,preloadColorStateLists,preloadDrawables预加载各种图像和颜色配置资源

  1. preloadSharedLibraries();
  private static void preloadSharedLibraries() {Log.i(TAG, "Preloading shared libraries...");System.loadLibrary("android");System.loadLibrary("compiler_rt");System.loadLibrary("jnigraphics");try {System.loadLibrary("sfplugin_ccodec");} catch (Error | RuntimeException e) {// tolerate missing sfplugin_ccodec which is only present on Codec 2 devices}}

4.2.2 createManagedSocketFromInitSocket

创建socke连接

4.2.3 runSelectLoop

主要通过linux poll机制,轮询监听socket客服端请求连接情况。 主要流程:

  1. 将本地socket服务创建的套接字文件描述符添加到被监测的文件描述符socketFDs中去
  2. 调用Os.poll,等待文件描述符就绪
  3. 如果超时,停留一段时间再监听
  4. 如果返回正常的,则判断请求类型,共三中类型:

4.1 则判断是socket Connect,则通过accept答应请求,创建一个客户端的连接 4.2 如果是闯将子应用请求,则返回创建命令 4.3 如果是断开请求,则断开该文件描述符与server的连接

关于linux poll机制,请参考linux poll,java层的使用跟native层机制完全一致。

Runnable runSelectLoop(String abiList) {ArrayList<FileDescriptor> socketFDs = new ArrayList<>();ArrayList<ZygoteConnection> peers = new ArrayList<>();//1\. 将创建socket生成的文件描述符,添加到数组socketFDs.add(mZygoteSocket.getFileDescriptor());peers.add(null);mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;while (true) {fetchUsapPoolPolicyPropsWithMinInterval();mUsapPoolRefillAction = UsapPoolRefillAction.NONE;int[] usapPipeFDs = null;StructPollfd[] pollFDs;if (mUsapPoolEnabled) {usapPipeFDs = Zygote.getUsapPipeFDs();pollFDs = new StructPollfd[socketFDs.size() + 1 + usapPipeFDs.length];} else {pollFDs = new StructPollfd[socketFDs.size()];}//2\. 初始化poll()的参数fdsint pollIndex = 0;for (FileDescriptor socketFD : socketFDs) {pollFDs[pollIndex] = new StructPollfd();pollFDs[pollIndex].fd = socketFD;pollFDs[pollIndex].events = (short) POLLIN;++pollIndex;}final int usapPoolEventFDIndex = pollIndex;...//3\. 阻塞等待请求事件int pollReturnValue;try {pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);} catch (ErrnoException ex) {throw new RuntimeException("poll failed", ex);}//超时if (pollReturnValue == 0) {// The poll timeout has been exceeded.  This only occurs when we have finished the// USAP pool refill delay period.mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;} else {boolean usapPoolFDRead = false;//4\. 轮询遍历检测client[],处理有就绪事件的文件描述符while (--pollIndex >= 0) {if ((pollFDs[pollIndex].revents & POLLIN) == 0) {continue;}if (pollIndex == 0) {//客户端connect连接请求,非数据发送和读取请求// Zygote server socketZygoteConnection newPeer = acceptCommandPeer(abiList);//接受客户端连接peers.add(newPeer);socketFDs.add(newPeer.getFileDescriptor());} else if (pollIndex < usapPoolEventFDIndex) {// Session socket accepted from the Zygote server sockettry {ZygoteConnection connection = peers.get(pollIndex);final Runnable command = connection.processOneCommand(this);// TODO (chriswailes): Is this extra check necessary?if (mIsForkChild) {//fork 子进程请求// We're in the child. We should always have a command to run at// this stage if processOneCommand hasn't called "exec".if (command == null) {throw new IllegalStateException("command == null");}return command;} else {//关闭连接请求// We're in the server - we should never have any commands to run.if (command != null) {throw new IllegalStateException("command != null");}// We don't know whether the remote side of the socket was closed or// not until we attempt to read from it from processOneCommand. This// shows up as a regular POLLIN event in our regular processing// loop.if (connection.isClosedByPeer()) {connection.closeSocket();peers.remove(pollIndex);socketFDs.remove(pollIndex);}}} catch (Exception e) {...}...} else {...}}...}...}}

5. 总结

从以上的分析可以得知,Zygote进程启动中承担的主要职责如下

  1. 创建AppRuntime,执行其start方法。
  2. 创建JVM并为JVM注册JNI方法。
  3. 使用JNI调用ZygoteInit的main函数进入Zygote的Java FrameWork层。
  4. 调用preload完成系统资源的预加载。主要包括preloadClasses,preloadResources,preloadDrawables,preloadSharedLibraries
  5. 通过ZygoteServer创建本地服务端socke连接(LocalServerSocket), 调用Os.socket, bindLocal绑定socket文件和进程
  6. 启动SystemServer进程。
  7. 通过runSelectLoop调用linux poll机制,阻塞等待客户端请求连接,创建应用,关闭连接请求

zygote启动过程相关推荐

  1. Android系统进程Zygote启动过程的源代码分析

    原文地址:http://blog.csdn.net/luoshengyang/article/details/6747696 Android应用程序框架层创建的应用程序进程具有两个特点,一是进程的入口 ...

  2. Android系统进程Zygote启动过程的源代码分析(3)

    Step 5. ZygoteInit.startSystemServer        这个函数定义在frameworks/base/core/java/com/android/internal/os ...

  3. 从源码角度看Android系统Zygote进程启动过程

    在Android系统中,DVM.ART.应用程序进程和SystemServer进程都是由Zygote进程创建的,因此Zygote又称为"孵化器".它是通过fork的形式来创建应用程 ...

  4. Zygote启动流程解析

    目录 1.什么是Zygote? 2. Zygote脚本启动 3.Zygote进程启动 1.什么是Zygote? Zygote是Android系统创建的第一个Java进程,它是所有Java进程的父进程. ...

  5. Dalvik VM进程系统(二):分析Zygote的启动过程

    Android系统进程启动流程 android系统的Zygote进程是所有android进程的父进程,包括SystemServer和各种应用进程都是通过Zygote进程fork出来的.Zygote(孵 ...

  6. Zygote和System进程的启动过程

    ##init脚本的启动 +------------+ +-------+ +-----------+ |Linux Kernel+--> |init.rc+-> |app_process| ...

  7. 笔记:Zygote和SystemServer进程启动过程

    简述 Android设备启动过程中,先是Linux内核加载完,接着Android中的第一个进程init启动,它会启动一些需要开机启动的进程. Zygote就是进程init启动起来的.Android中所 ...

  8. Android 系统(12)---Zygote进程启动过程

    android系统进程启动流程 android系统的Zygote进程是所有android进程的父进程,包括SystemServer和各种应用进程都是通过Zygote进程fork出来的.Zygote(孵 ...

  9. Zygote进程启动过程源代码分析

    Zygote进程介绍 在Android系统中,存在不同的服务,这些服务可以分为: Android系统借用Binder通信机制实现了C/S架构设计,客户端应用程序如需要实现某些功能,只需请求指定的服务, ...

最新文章

  1. %fplot('Untitled1',[-1,2])画图
  2. video视频播放以及主流浏览器兼容
  3. ubuntu 运行python subprocess 出现/bin/sh: 1: source: not found 错误
  4. DirectX 3D学习笔记(一)
  5. Androidstudio坑
  6. [HNOI2012]排队
  7. 另辟蹊径:从其他角度去解决数据库问题
  8. opengl 如何加阴影_零基础如何2个月上岗C++工程师(内附资料)
  9. NOIP1998车站
  10. Java之品优购课程讲义_day08(7)
  11. dataframe 如何选中某列的一行_Spark中的RDD、DataFrame和DataSet讲解
  12. ImportError: /home/cyj/anaconda2/bin/../lib/libstdc++.so.6: version `GLIBCXX_3.4.21' not found
  13. linux 命令无法Tab补全,命令参数无法补全
  14. 白度云下载到存储卡的视频看不了_苹果手机从百度网盘下载的视频如何保存到相册,还有不知道的吗?...
  15. 网络游戏前后端时间同步
  16. Python 轻松解决从 K 个字符串数组中任意取一个字符串,按顺序拼接,列出所有可能的字符串组合。(对比用库和不用库的方法)
  17. 【学习技巧】——怎样改掉学习上的坏习惯
  18. Nature子刊 | 地下水固碳速率与寡营养海洋系统固碳速率相近
  19. 如何获取彩色图像中的主色彩
  20. 【限时干货】数据圈火爆的数据产品文章全集

热门文章

  1. oracle中的日期函数
  2. python实现离散点图画法
  3. 招商头条:2018成都快递业中西部第一;合肥高新区30个项目签约125亿;芯盾时代完成3亿元融资
  4. poi对excel进行读取
  5. poi读取excel表
  6. php 调用java接口
  7. 编译出现错误,想知道为什么错误
  8. 【IntelliJ IDEA】编码设置终极版
  9. 闽江学院计算机系微博,我校计控学子在第13届中国大学生计算机设计大赛中勇创佳绩...
  10. 个性化lightswitch登录屏幕(附源码)