zygote 概述

在Android系统中,DVM(Dalvik虚拟机)、应用程序进程以及运行系统的关键服务的SystemServer进程都是由Zygote进程来创建的,我们也将它称为孵化器。
zygote的英文名为受精卵,由于他能不断孵化进程,因此也是名字的由来。
它通过fock(复制进程)的形式来创建应用程序进程和SystemServer进程,由于Zygote进程在启动时会创建DVM,因此通过fock而创建的应用程序进程和SystemServer进程可以在内部获取一个DVM的实例拷贝

init.rc中配置

在init进程创建时,会读取 init.zygote.rc 中的配置文件

init.zygote.rc

这个是在rc文件中的配置,下面我们会一次介绍各个命令的作用,这些命令具体都会在源码分析中看到

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-serverclass mainpriority -20user rootgroup root readprocsocket zygote stream 660 root systemonrestart write /sys/android_power/request_state wakeonrestart write /sys/power/state ononrestart restart audioserveronrestart restart cameraserveronrestart restart mediaonrestart restart netdonrestart restart wificondwritepid /dev/cpuset/foreground/tasks

service

service指令告诉系统将zygote程序加入到系统服务中,基本语法如下

service service_name 可执行程序的路径 可执行程序自身所需的参数列表

这里我们的服务是 zygote 他的程序路径在 /system/bin/app_process ,后面那一堆,就是程序执行的参数,在 app_process 的main函数中会从argc和argv中取出

-Xzygote

作为虚拟机启动时所需要的参数,在AndroidRuntime.cpp中的 startVm() 函数中调用 JNI_CreateJavaVM 使用到

/system/bin

代表虚拟机程序所在目录,因为 app_process 可以不和虚拟机在一个目录,所以 app_process 需要知道虚拟机所在的目录

–zygote

指明以 ZygoteInit 类作为入口,否则需要指定需要执行的类名

–start-system-server

仅在有 --zygote 参数时可用,告知 ZygoteInit 启动完毕后孵化出的第一个进程是 SystemServer

其他命令

后续的命令是和socket相关,名称、类型、端口号、重启后的操作等等

app_process

该文件在 frameworks/base/cmds/app_process 中,找到 main 函数

//前面的操作都是对参数进行一些赋值等等,就是之前的命令中配置的参数
......
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.");}

这个 runtime 是 AppRuntime

AppRuntime.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);//JNI方法的注册if (startReg(env) < 0) {ALOGE("Unable to register all android natives\n");return;}......char* slashClassName = toSlashClassName(className != NULL ? className : "");jclass startClass = env->FindClass(slashClassName);if (startClass == NULL) {......} else {jmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");if (startMeth == NULL) {......} else {env->CallStaticVoidMethod(startClass, startMeth, strArray);}}
}

对虚拟机和JNI方法的一些注册后,通过 CallStaticVoidMethod 来调用传过来的类名的main函数,我们传递过来的类名是 com.android.internal.os.ZygoteInit

ZygoteInit.main

该文件在 frameworks/base/core/java/com/android/internal/os 中

public static void main(String argv[]) {ZygoteServer zygoteServer = new ZygoteServer();......final Runnable caller;try {......zygoteServer.registerServerSocket(socketName);preload(); ......if (startSystemServer) {Runnable r = forkSystemServer(abiList, socketName, zygoteServer);if (r != null) {r.run();return;}}caller = zygoteServer.runSelectLoop(abiList);} catch (Throwable ex) {......} finally {zygoteServer.closeServerSocket();}if (caller != null) {caller.run();}
}

registerServerSocket : 用于注册socket
preload : 预加载类和资源
forkSystemServer : 启动system_server
runSelectLoop : socket的处理

registerServerSocket

注册一个socket,来接收启动新进程一些命令操作,建立socket通道,zygote作为通信的服务端,用于响应客户端请求

void registerServerSocket(String socketName) {if (mServerSocket == null) {int fileDesc;final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;try {String env = System.getenv(fullSocketName);fileDesc = Integer.parseInt(env);} catch (RuntimeException ex) {throw new RuntimeException(fullSocketName + " unset or invalid", ex);}try {FileDescriptor fd = new FileDescriptor();fd.setInt$(fileDesc);mServerSocket = new LocalServerSocket(fd);} catch (IOException ex) {throw new RuntimeException("Error binding to local socket '" + fileDesc + "'", ex);}}
}

fileDesc 是一个文件描述符

最终会构造一个 LocalServerSocket ,创建socket的本地服务端

关于socket

在Linux系统中,所有的资源都可以看做是文件,socket也是,所以传递了一个文件描述符来构建socket

注意
socket也是IPC的一种方式
socket中有两种方式进行通信

阻塞式

使用listen()监听某个端口,调用read()读取数据,没有数据时,会一直等待

非阻塞式

使用select()将需要检测的文件描述符作为 select() 函数的参数,当文件描述符上出现新的数据后,自动触发一个中断,然后在中断处理函数中再去读取指定文件描述符的数据

LocalServerSocke 用的是第二种 非阻塞式,读取的文件为 /dev/socket/zygote

LocalServerSocke 对应的客户端

前面的代码讲解的是关于socket服务端的,有服务端那就有客户端

它对应的客户端是进程 SystemServer 中创建出来的

preload

为了能够让 zygote 孵化进程进行的资源加载,预加载通用类、drawable和color资源、openGL以及共享库以及WebView,用于提高app启动效率

static void preload() {//预加载位于/system/etc/preloaded-classes文件中的类preloadClasses();//预加载资源,包含drawable和color资源preloadResources();//预加载OpenGLpreloadOpenGL();//通过System.loadLibrary()方法,//预加载"android","compiler_rt","jnigraphics"这3个共享库preloadSharedLibraries();//预加载 文本连接符资源preloadTextResources();//仅用于zygote进程,用于内存共享的进程WebViewFactory.prepareWebViewInZygote();
}

forkSystemServer

这个小节介绍SystemService的启动过程

fork

fork是Linux中的一个系统调用,作用是复制当前进程,除了进程ID不同,两个进程信息完全一致

新进程被创建后,两个进程将共享已经分配的内存空间,直到其中一个进程需要向内存写入数据的时候,系统才会复制一份目标地址空间,并将写的数据写入到新的地址中,这就是 copy-on-write 机制,“仅当写的时候才复制”,可以节省共享物理内存,调用一次,返回两次,返回值有3种类型

● 父进程中,fork返回新创建的子进程的pid
● 子进程中,fork返回0
● 当出现错误时,fork返回负数(当进程数超过上限或者系统内存不足时会出错)

fork()的主要工作是寻找空闲的进程号pid,然后从父进程拷贝进程信息,例如数据段和代码段,fork()后子进程要执行的代码等。 Zygote进程是所有Android进程的母体,包括system_server和各个App进程。zygote利用fork()方法生成新进程,对于新进程A复用Zygote进程本身的资源,再加上新进程A相关的资源,构成新的应用进程A

copy-on-write原理

写时拷贝是指子进程与父进程的页表都所指向同一个块物理内存,fork过程只拷贝父进程的页表,并标记这些页表是只读的。父子进程共用同一份物理内存,如果父子进程任一方想要修改这块物理内存,那么会触发缺页异常(page fault),Linux收到该中断便会创建新的物理内存,并将两个物理内存标记设置为可写状态,从而父子进程都有各自独立的物理内存

为什么要fork

因为两个进程存在大量的共享程序,如果使用fork,可以节省大量的共享内存

SystemServer的启动

private static Runnable forkSystemServer(String abiList, String socketName,ZygoteServer zygoteServer) {......String args[] = {"--setuid=1000","--setgid=1000","--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,3001,3002,3003,3006,3007,3009,3010","--capabilities=" + capabilities + "," + capabilities,"--nice-name=system_server","--runtime-args",//注意这里的类名,它会去装载这个类"com.android.server.SystemServer",};ZygoteConnection.Arguments parsedArgs = null;int pid;try {parsedArgs = new ZygoteConnection.Arguments(args);ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);//主要的fork操作,会调用到native层的代码进行 forkpid = Zygote.forkSystemServer(parsedArgs.uid, parsedArgs.gid,parsedArgs.gids,parsedArgs.debugFlags,null,parsedArgs.permittedCapabilities,parsedArgs.effectiveCapabilities);} catch (IllegalArgumentException ex) {throw new RuntimeException(ex);}/* For child process 这里代表新进程*/if (pid == 0) {if (hasSecondZygote(abiList)) {waitForSecondaryZygote(socketName);}zygoteServer.closeServerSocket();return handleSystemServerProcess(parsedArgs);}return null;}

在 pid == 0 的节点中,关闭socket服务端(注意这里关闭的是从父进程复制过来的子进程里的socket,因为子进程不需要这个socket),
handleSystemServerProcess 函数的作用是调用 SystemServer 的main函数以及一些配置工作(调用新进程指定的class的main函数)

一旦 SystemServer 的配置工作完成后,就会从 SystemServer 的 main 函数开始运行

到此,调用main后,新进程就完全脱离了 zygote 进程的孵化过程了,成为一个真正的应用进程

runSelectLoop

socket创建后就是进入读操作了

Runnable runSelectLoop(String abiList) {ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();//mServerSocket 是socket通信中的服务端,即zygote进程。保存到fds[0]fds.add(mServerSocket.getFileDescriptor());peers.add(null);//无限循环等待AMS的请求while (true) {StructPollfd[] pollFds = new StructPollfd[fds.size()];for (int i = 0; i < pollFds.length; ++i) {pollFds[i] = new StructPollfd();pollFds[i].fd = fds.get(i);pollFds[i].events = (short) POLLIN;}try {//处理轮询状态,当pollFds有事件到来则往下执行,否则阻塞在这里Os.poll(pollFds, -1);} catch (ErrnoException ex) {throw new RuntimeException("poll failed", ex);}for (int i = pollFds.length - 1; i >= 0; --i) {if ((pollFds[i].revents & POLLIN) == 0) {continue;}// 第一个就是刚才的操作 fds.add(mServerSocket.getFileDescriptor());if (i == 0) {//接收客户端发送过来的connect()操作,Zygote作为服务端执行accept()操作。 再后面客户端调用write()写数据,Zygote进程调用read()读数据ZygoteConnection newPeer = acceptCommandPeer(abiList);peers.add(newPeer);fds.add(newPeer.getFileDesciptor());} else {try {ZygoteConnection connection = peers.get(i);final Runnable command = connection.processOneCommand(this);......} catch (Exception e) {......}}}}
}

在读取数据的操作中的 processOneCommand 里,最为核心的方法就是 Zygote.forkAndSpecialize,看名字就应该知道他的作用就是fork进程,runSelectLoop 函数随时待命,当接收到请求创建新进程请求时立即唤醒并执行相应工作

总结

zygote主要做了一下几件事:

  1. 创建AppRuntime并调用start方法,启动zygote进程
  2. 创建java虚拟机并未java虚拟机注册JNI方法
  3. 通过JNI调用 zygoteinit的main函数进入zygote的java框架层
  4. 通过 registerZygoteSocket方法创建服务端 Socket,并通过 runSelectLoop 方法等待AMS的请求来创建新的应用进程
  5. 启动 SystemServer进程

zygote的main函数的流程可以简化为下

public static void main(String argv[]) {try {caller = zygoteServer.runSelectLoop(abiList);....} catch (RuntimeException ex) {closeServerSocket();throw ex;}if (caller != null) {caller.run();}
}

注意,之前在分析acceptCommandPeer部分的时候,父进程是直接返回null的,新进程是返回一个runnable,因此,这部分的代码在不同进程中的执行是不同的.对于父进程也就是Zygote进程,他是一直循环运行的,对于新进程,会完成启动操作后停止循环并调用目标类的main函数

  1. 系统启动时init进程会创建Zygote进程,Zygote进程负责后续Android应用程序框架层的其它进程的创建和启动工作。
  2. Zygote进程会首先创建一个SystemServer进程,SystemServer进程负责启动系统的关键服务,如包管理服务PackageManagerService和应用程序组件管理服务ActivityManagerService。
  3. 当我们需要启动一个Android应用程序时,ActivityManagerService会通过Socket进程间通信机制,通知Zygote进程为这个应用程序创建一个新的进程。

Android学习之zygote启动流程相关推荐

  1. android zygote启动流程,Android zygote启动流程详解

    对zygote的理解 在Android系统中,zygote是一个native进程,是所有应用进程的父进程.而zygote则是Linux系统用户空间的第一个进程--init进程,通过fork的方式创建并 ...

  2. LIteOS学习笔记-7LiteOS启动流程与编译流程

    LIteOS学习笔记-7LiteOS启动流程与编译流程 LiteOS启动流程 1. 启动方式 2. 启动流程 硬件初始化 内核初始化 调试串口初始化 尝试进行网络连接 启动任务调度 LiteOS编译流 ...

  3. 【Android 源码学习】Zygote启动原理

    Android 源码学习 Zygote启动原理 望舒课堂 Zygote进程启动原理学习记录整理. Zygote简介 Zygote是进程在init进程启动时创建的,进程本身是app_process,来源 ...

  4. Android FM模块学习之一 FM启动流程

    转自:http://blog.csdn.net/tfslovexizi/article/details/41283743 最近在学习FM模块,FM是一个值得学习的模块,可以从上层看到底层.上层就是FM ...

  5. Android应用程序进程启动流程

    在学习应用程序进程启动流程前,先要弄清楚系统启动流程,如果有不清楚的同学,建议先看下以前博主的文章: Android系统启动(上篇)_AD钙奶-lalala的博客-CSDN博客 Android系统启动 ...

  6. Android系统完整的启动流程

    Android系统完整的启动过程,从系统层次角度可分为 Linux 系统层.Android 系统服务层.Zygote进程模型三个阶段:从开机到启动 Home Launcher 完成具体的任务细节可分为 ...

  7. Android 13.0 SystemUI 启动流程

    1.手机开机后,Android系统首先会创建一个Zygote(核心进程). 2.由Zygote启动SystemServer. 3.SystemServer会启动系统运行所需的众多核心服务和普通服务.以 ...

  8. Android 源码 PackageManagerService 启动流程分析

    <Android 源码 installPackage 流程分析>一节着重分析了 apk 安装流程,接下来我们分析 PackageManagerService 启动时都做了些什么? 执行 P ...

  9. Android入门之APP启动流程

    俗话说,要想优化好,流程不可少.作为一款App的开发者,首先要把它的启动流程做好了,简单明了的启动流程不仅拥有很好的体验感,还能获得更多用户对App的肯定.本篇文章就带大家了解下app启动流程的三个进 ...

最新文章

  1. 接口中可以有静态方法吗?
  2. linux命令行使用for循环,小弟我使用过的Linux命令之for - Bash中的For循环
  3. ASP.NET MVC: 构建不带 Web 窗体的 Web 应用程序(转载)
  4. 面向Linux的10款最佳剪贴板管理器
  5. Maven学习总结(53)——利用Maven插件构建镜像进行持续交付中的版本号管理
  6. PHP empty操作记录
  7. PTA L2-006 树的遍历-二叉树的后序遍历+中序遍历,输出层序遍历 团体程序设计天梯赛-练习集...
  8. 分层结构的生活例子_详解软件分层架构设计、工作原理、实例以及具体架构
  9. arrays中copyof复制两个数组_Java教程分享之数组知识梳理
  10. python读取MNIST image数据
  11. 有N个软件包未被升级
  12. Caused by: java.lang.ClassNotFoundException: Didn’t find class on path apk Android Studio解决方案
  13. 在线教育源码 知识付费平台源码 PC+H5+后台管理端 教育课程源码
  14. 为什么直播礼物特效越来越流行?
  15. java fastfds操作文件
  16. CESM mpirun noticed that process rank 1 with PID 0 on node ubuntu exited on signal 11
  17. win10 右键菜单管理
  18. 西安周边旅游++军事纪实
  19. 食品和饮料销售预测分析
  20. 离散数学复习:二元关系

热门文章

  1. 在Ubuntu安装完成后更改默认的语言LANG,出现Cannot set LC_CTYPE to default locale: No such file or directory错误的解决办法
  2. OpenCV笔记02:用cv2.imread函数读取图片
  3. 神经网络入门(个人理解)
  4. python字符串的定界符可以是_Python中,字符串不能用以下哪个符号作为定界符(): \|'|'''|;...
  5. 怎么用计算机按反三角函数的导数,反三角函数求导过程
  6. Mac安装brew及使用
  7. python透视变换原理_python cv2中的透视变换
  8. ASCII转十六进制小工具
  9. 如何批量生成JAN13条码
  10. DELL(i7版本)麦克风杂音问题解决方案