IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android Zygote -> 正文阅读

[移动开发]Android Zygote

1.Zygote

Zygote译为“受精卵”。在Android中,负责孵化新进程的这个进程就叫Zygote,安卓上其他的应用进程都是由它孵化的。由于安卓是Linux内核,安卓系统上运行的一切程序都是放在Dalvik虚拟机上的,Zygote也不例外,事实上,它是安卓运行的第一个Dalvik虚拟机进程。既然Zygote负责孵化其他的安卓进程,那么它自己是由谁孵化的呢?因为Android是基于Linux内核,那么Zygote当然就是Linux内核启动的用户级进程Init创建的了。

Zygote进程在启动时会创建一个Dalvik虚拟机实例,每当它孵化一个新的应用程序进程时,都会将这个Dalvik虚拟机实例复制到新的应用程序进程里面去,从而使每一个应用程序进程都有一个独立的Dalvik虚拟机实例。试想,如果每个应用程序在启动时都要单独运行和初始化一个虚拟机,会大大降低系统性能,因此Android首先创建一个zygote虚拟机,然后通过它孵化出其他的虚拟机进程,进而共享虚拟机内存和框架层资源,这样就大幅度提高了应用程序的启动和运行速度。

?

zygote的作用:

?①创建并启动SystemServer进程

②fork新的应用进程

?

2.zygote的启动

zygote是由Init进程(pid=0)解析init.rc脚本启动的。Zygote进程在Init进程启动过程中被以service服务的形式启动。

从rc文件中可以看出:

①zygote的进程优先级是最高的,priority被设成-20,主要是因为Android的启动非常依赖于SystemServer的启动,而SystemServer也是zygote fork出来的,因此需要将zygote优先级设成最高的。

②zygote在启动时创建了两个socket(zygote和usap_pool_primary),用于进程间的通信。

③zygote是Android非常重要的核心服务之一,当其因异常退出后,Init会将其重新启动,并将一些相关的节点和服务重新设置。

zygote的启动实际上分成了两部分:SystemServer的启动、ZygoteServer的启动。两者前面是在同一个进程中执行的,当虚拟机创建完成后才正式分离。

?

zygote是通过app_process启动,入口就是app_process的main函数:

#frameworks/base/cmds/app_process/app_main.cpp

int main(int argc, char* const argv[]) {

? ? AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));

? ? …

? ??runtime.start( "com.android.internal.os.ZygoteInit", args, zygote);

? ? …

}

创建AndroidRuntime对象(AndroidRuntime继承自AppRuntime),这里面主要的动作就是启动虚拟机。然后在虚拟机中运行ZygoteInit,ZygoteInit是java写的,即这一步Zygote就从native世界进入到了Java世界。接下来的流程如下图:

835bb781a5794b25bb27a8abf0cd409b.jpg

?Zygote的java世界入口就是ZygoteInit类的main方法:

#frameworks/base/core/java/com/android/internal/os/ZygoteInit.java:

public static void main(String argv[]) {?

? ? ……

? ? zygoteServer = new ZygoteServer( isPrimaryZygote); //1.创建ZygoteServer

? ? ??zygoteServer.registerServerSocket( socketName);?//2.创建一个Server端的Socket

? ? preload(bootTimingsTraceLog); //3.加载进程的资源和类

? ? if (startSystemServer) {

? ? ? ? Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer); //4.启动SystemServer

? ? ? ? if (r != null) {

? ? ? ? ? ? r.run();

? ? ? ? ?? ?return;

? ? ? ? }

? ??}

? ? //5.启动一个死循环监听来自Client端的消息

? ? caller = zygoteServer.runSelectLoop(abiList);

? ? if (caller != null) {

? ? ? ? caller.run();

? ? }

}

ZygoteInit的main()方法是Android启动中的第一个Java进程的主方法。它主要完成几件事情:

①注册一个socket

在main方法里,通过registerServerSocket方法来创建一个Server端的socket,这个name为zygote的socket用于等待ActivityManagerService请求Zygote来创建新的应用程序进程。

Zygote作为孵化器,跟其他进程间的通讯不是通过binder,而是通过socket。一旦有新进程需要运行,系统会通过这个socket跟Zygote通讯,由zygote完成进程孵化过程。

②预加载各类资源

preload函数用于加载虚拟机运行时所需的各类资源。

在ZygoteInit中,一般无特殊原因,都会调用preload()方法将Android Java层应用常用的资源预加装,其原因是:

1)Android是基于Linux系统开发的,底层就是Linux操作系统,而Linux进程的fork机制有一个特点,就是写时拷贝机制,即fork出来的进程最初是共用一块内存,只有当发生写操作时,才会将对应的内存块进行拷贝修改,而一些只读的内存块则会所有fork出来的进程共享。

2)preload()方法所加装的东西主要有以下几类:常用类文件、Resources资源、HALs资源、opengl、webview等。这些资源一般都是只读的,不会进行修改,而且是很多应用都可能会用到的。因此预先加载后所有由zygote fork出来的应用进程都能共用一块内存。

0fe012d22aa34373ac026fe4da44cd21.jpg

?通过上图可以很容易理解在Zygote进程预加载系统资源后,然后通过它孵化出其他的虚拟机进程,进而共享虚拟机内存和框架层资源,这样大幅度提高应用程序的启动和运行速度。

③启动SystemServer

b9ae6f20438346789ed239f35022cbfc.jpg?4718c8314bc94f2c9415d97e9834f27f.jpg

?SystemServer是通过forkSystemServer()方法fork出来并开始执行。

private static Runnable forkSystemServer(String abiList, String socketName, ZygoteServer zygoteServer) {

? ? int pid;

? ? pid = Zygote.forkSystemServer(

? ? ? ? ? ? ? ? parsedArgs.mUid, parsedArgs.mGid,

? ? ? ? ? ? ? ? parsedArgs.mGids,

? ? ? ? ? ? ? ? parsedArgs.mRuntimeFlags,

? ? ? ? ? ? ? ? null,

? ? ? ? ? ? ? ? parsedArgs.mPermittedCapabilities,

? ? ? ? ? ? ? ? parsedArgs.mEffectiveCapabilities);

? ? if (pid == 0) { //子进程

? ? ? ? zygoteServer.closeServerSocket();

? ? ? ? return handleSystemServerProcess( parsedArgs);

? ? }

? ? return null;

}

forkSystemServer()中主要做了两件事:①调用Zygote.forkSystemServer()方法fork一个新的进程出来。②fork()后的子进程是SystemServer进程,等待zygote的启动完成,然后执行真正的SystemServer代码。

// Zygote.java

public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {

? ? ZygoteHooks.preFork(); //将Daemons线程关掉,虚拟机进行一些copy前的处理

? ? // Resets nice priority for zygote process.

? ? resetNicePriority(); //将进程的优先级设回普通的级别

? ? int pid = nativeForkSystemServer( uid, gid, gids, runtimeFlags, rlimits, permittedCapabilities, effectiveCapabilities); //调用native层方法fork一个新的进程。该方法主要调用Linux的fork系统调用,fork一个新的进程,子进程的pid为0,父进程返回子进程的pid值

? ? if (pid == 0) { //子进程

? ? ? ? Trace.setTracingEnabled(true, runtimeFlags);

? ? }

? ? ZygoteHooks.postForkCommon(); //重新启动Daemons线程,虚拟机进行copy后的处理

? ? return pid;

}

注:fork操作是不会将进程中所有线程拷贝的,只会拷贝当前线程。

Daemons线程是指:①HeapTaskDaemon;②ReferenceQueueDaemon;③FinalizerDaemon;④FinalizerWatchdogDaemon。平时在trace文件(ANR或者其他方式获取的trace文件)中看到Java应用进程都会有这4个线程,这4个线程就是在这里创建的。

再回到ZygoteInit.forkSystemServer()方法,此时两个进程都会返回到此处,然后两个进程到这就分道扬镳了。SystemServer进程将ZygoteServer的socket关闭,然后调用handleSystemServerProcess()方法去执行SystemServer的源码。

handleSystemServerProcess()方法的核心代码:

createSystemServerClassLoader();

ClassLoader cl = sCachedSystemServerClassLoader;

?if (cl != null) {

? ? ? Thread.currentThread().setContextClassLoa der(cl);

?}

?return ZygoteInit.zygoteInit( parsedArgs.mTargetSdkVersion,parsedArgs.mRemainingArgs, cl);

先获取classloader,并将其设置为root classloader。然后调用ZygoteInit.zygoteInit()方法去执行。

public static final Runnable zygoteInit(int targetSdkVersion, String[] argv,?ClassLoader classLoader) {

? ? RuntimeInit.commonInit();

? ? ZygoteInit.nativeZygoteInit(); //为进程创建Binder的环境(ProcessStatae)

? ? return RuntimeInit.applicationInit( targetSdkVersion, argv, classLoader); //执行传参进来的类的main()方法,这里传进来的类就是com.android.server.SystemServer

}

ZygoteInit.forkSystemServer()返回一个runnable,返回到ZygoteInit.main()方法后,SystemServer进程就会继续执行这个runnable。

zygote在fork进程后,使用runnable的方式返回到main()方法后才执行的原因?减少线程堆栈的长度,因为fork后进行执行的内容与之前zygote的堆栈内容并无关系,因此不需要为每个进程都保留前面的堆栈信息,而且还能减少进程的堆栈容量限制。

④进入Loop循环 ZygoteServer.runSelectLoop

当zygote进程返回到main()方法后,就会继续执行ZygoteServer.runSelectLoop()方法,这个方法是一个死循环,是不断进行循环执行命令的方法。执行runSelectLoop()方法是为了等待消息去创建应用进程。

runSelectLoop()方法主要做两件事:

①每次循环都重新构建监听文件列表,主要是ZygoteServer的socket文件(ZygoteServer的socket和其他应用进程连接过来的socket)和usap文件节点。

②监听文件列表,并从中获取命令执行。

?

一张图看看Zygote的启动流程:

dbeb367eb65049e0889173200bd8aaf0.png

84ca3d8d2d704fc994fe1091a791448e.jpg

?

3.总结

Zygote创建Java世界的步骤:

①第一天:创建AppRuntime对象,并调用它的start。此后的活动则由AppRuntime来控制。

②第二天:调用startVm创建Java虚拟机,然后调用startReg来注册JNI函数。

③第三天:通过JNI调用com.android.internal.os.ZygoteInit类的main函数,从此进入了Java世界。然而在这个世界刚开创的时候,什么东西都没有。

④第四天:调用registerZygoteSocket,通过这个函数,它可以响应子孙后代的请求。同时Zygote调用preloadClasses和preloadResources,为Java世界添砖加瓦。

⑤第五天:Zygote觉得自己工作压力太大,便通过调用startSystemServer分裂一个子进程system_server来为Java世界服务。

⑥第六天:Zygote完成了Java世界的初创工作,它已经很满足了。下一步该做的就是调用runSelectLoopMode后,便沉沉地睡去了。

⑦以后的日子:Zygote随时守护在我们的周围,当接收到子孙后代的请求时,它会随时醒来,为它们工作。

⑧有一天,他的孩子SystemServer发送了个请求,Zygote苏醒,Zygote调用ZygoteConnection的runOnce函数。在runOnce方法中Zygote分裂了一个子进程(App进程),然后在调用handleChildProc方法在子进程中进行处理。

⑨然后继续沉睡,等待请求进行下一次分裂。

6298a2cc63874bd9b3a42133eeaeaac6.png

?

?

?

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章           查看所有文章
加:2022-06-21 21:30:36  更:2022-06-21 21:31:22 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 2:25:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码