• 对zygote的理解


    一、 zygote的作用

      1. 启动SystemServer
        SystemServer需要用于zygote准备好的一些系统资源,比如常用类、注册的JNI函数、主题资源、共享库等等,直接从zygote那继承过来,SystemServer就不用重新再加载一遍,这样对性能有很大的提升。
      1. 孵化应用进程

    在这里插入图片描述
    Loop循环就是不停的接收消息,处理消息;处理的消息可以能messageQueue里面的消息,也可能是binder驱动那边传过来的。

    二、Zygote的启动流程

    1. zygote进程是怎么启动的
    Init进程为Linux启动后用户空间的第一个进程,Init进程启动之后,首先会加载init.rc启动配置文件,然后会查看启动配置文件定义了哪些系统服务需要启动的,zygote就是要启动的服务之一,通过fork + execve 进行启动zygote进程
    启动zygote进程相关的配置:
    在这里插入图片描述
    第一行命令语句说明:

    • 红色部分zygote: 表示service的名称
    • 蓝色部分/system/bin/app_process:表示zygote程序路径
    • 黄色部分-Xzygote /system/bin --zygote --start-system-server: 表示程序执行的参数
    • Xzygote:作为虚拟机启动时所需要的参数,在AndroidRuntime.cpp中的 startVm() 函数中调用 JNI_CreateJavaVM 使用到
    • /system/bin:代表虚拟机程序所在目录,因为 app_process 可以不和虚拟机在一个目录,所以 app_process 需要知道虚拟机所在的目录
    • -zygote:指明以 ZygoteInit 类作为入口,否则需要指定需要执行的类名
    • –start-system-server:仅在有 --zygote 参数时可用,告知 ZygoteInit 启动完毕后孵化出的第一个进程是 SystemServer
    • 其他命令:后续的命令是和socket相关,名称、类型、端口号、重启后的操作等等

    启动进程
    (1) fork + handle

    pid_t pid =fork();
    if (pid == 0) {
    	// child process
    } else {
    	// parent process
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (2) fork + execve

    pid_t pid =fork();
    if (pid == 0) {
    	// child process
    	execve(path, argv, env);
    } else {
    	// parent process
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2. Zygote进程启动之后做了什么?

    • Zygote的Native世界
      (1)启动Android虚拟机(初始化运行环境,创建jvm)
      (2)注册Android的JNI函数
      (3)进入Java世界(调用zygoteinit.main)
      在这里插入图片描述

    • Zygote的Java世界
      (1)预加载资源 Proload Resources,比如常用类、主题资源、共享库等–>加快进程启动
      (2)socket–>让别人通知我
      (3)启动System Server(单独运行在一个进程中)
      (4)LOOP循环
      在这里插入图片描述

    boolean runOnce() {
    	String[] args = readArgumentList();  //读取参数列表
    	int pid = Zygote.forkAndSpecialize(); //启动子进程
    	if (pid == 0) {
    		// in child 
    		handleChildProc(args, ...);  //在子进程中进行相应的工作,执行该语句之后会执行ActivityThread.man()函数
    		return true;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    要注意的细节

    • Zygote 进行fork的时候要单线程,为了避免造成死锁或者状态不一致等问题
    • Zygote的IPC(跨进程通信)没有采用binder,采用的是本地socket通信

    问题: 谈谈你对zygote的理解?
    What:zygote的作用是什么?
    答:(1)启动System server进程,是用于管理整个Java framework层,包含ActivityManager,PowerManager等各种系统服务;
    (2)孵化其他应用程序进程

    How:zygote的启动流程是什么?
    答:Zygote进程是由Init进程解析init.zygote.rc文件启动的,zygote所对应的可执行程序app_process,所对应的源文件是App_main.cpp,进程名为zygote。

    首先执行App_main.cpp的main()函数,在这里会执行以下步骤:

    1.解析命令参数,主要是–zygote 和–start-system-server。

    2.调用AppRuntime.start(“com.android.internal.os.ZygoteInit”,…),启动虚拟机,注册JNI函数;

    3.通过JNI调用ZygoteInit.java的main()方法,从这里开始进入Java的世界;

    运行Zygote的main方法,主要执行以下步骤:

    预加载 preloadClasses()、preloadResources()、preloadSharedLibraries()
    forkSystemServer,最终会调用SystemServer.java的main()方法;
    创建ZygoteService,进入runSelectLoop;

    zygote的启动流程简单说明:
    1)创建Java虚拟机;
    2)为Java虚拟机注册native方法;
    3)在com.android.internal.os.ZygoteInit中调用java类的主要方法;
    4)加载ZygoteInit class;
    5)注册zygote socket;
    6)加载preload class;
    7)加载preload 资源文件;
    8)调用Zygote::forkSystemServer,fork一个新的进程,调用SystemServer的main方法,从而进入到java层的system_server进程的初始化流程中;

    Why:zygote的工作原理是什么?(如何孵化进程以及如何进行通信)

    问题1:孵化应用进程这种事为什么不交给SystemServer来做,而专门设计一个Zygote?
    答:我们知道,应用在启动的时候需要做很多准备工作,包括启动虚拟机,加载各类系统资源等等,这些都是非常耗时的,如果能在zygote里就给这些必要的初始化工作做好,子进程在fork的时候就能直接共享,那么这样的话效率就会非常高。这个就是zygote存在的价值,这一点呢SystemServer是替代不了的,主要是因为SystemServer里跑了一堆系统服务,这些是不能继承到应用进程的。所以给SystemServer和应用进程里都要用到的资源抽出来单独放在一个进程里,也就是这的zygote进程,然后zygote进程再分别孵化出SystemServer进程和应用进程。

    问题2: Zygote的IPC通信机制为什么使用socket而不采用binder
    答:主要原因是因为Zygote进行fork的时候要是单线程,父进程binder线程有锁,然后子进程的主线程一直在等其子线程(从父进程拷贝过来的子进程)的资源,但是其实父进程的子进程并没有被拷贝过来,造成死锁,所以fork不允许存在多线程。而非常巧的是Binder通讯偏偏就是多线程,所以干脆父进程(Zygote)这个时候就不使用binder线程。

      当在父进程中fork子进程的时候,父进程的线程也会被拷贝到子进程当中,但是这个时候线程已经不是一个线程了,而是一个对象,任何线程的特性都不再存在。父进程线程持有一个锁对象,那么在子进程中这个锁也会被复制过去,在子进程中如果想要竞争获取这个锁对象肯定是拿不到的,因为在对象头中,这个是加锁的,那么就会造成死锁;

      本质原因是,在fork过程中如果采用binder,binder是并发的,binder并发过程中会有这个对象锁,那么对象锁正在锁的过程中如果fork的话,那这个对象锁是没有办法解锁的,在fork产生的新进程里面,这个进程就会出现对象处于一个锁的状态,而且没有人去给它解锁,所以这种情况下就会出现对象死锁状态

  • 相关阅读:
    ARM学习(29)NXP 双coreMCU IMX1160学习----NorFlash 启动引脚选择
    数组第 k 大子序列
    抛砖引玉:Redis 与 接口自动化测试框架的结合
    【Spring笔记02】Spring中的IOC容器和DI依赖注入介绍
    【JS】Chapter8-Dom 事件进阶
    IOS渲染流程之提交图层数据至RenderThread进程
    centos安装nginx(root操作)
    前端面试题:在浏览器输入url回车后发生什么?浏览器渲染页面详细流程
    js中事件委托和事件绑定之间的区别
    C++实现观察者模式(包含源码)
  • 原文地址:https://blog.csdn.net/xuyin1204/article/details/127979407