前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android框架简介--启动过程--Zygote

Android框架简介--启动过程--Zygote

作者头像
小蚂蚁与大象
发布2022-03-24 08:39:11
5770
发布2022-03-24 08:39:11
举报
文章被收录于专栏:构建FFmpeg Android播放器

Zygote是做什么的

Zygote简单地说主要作用是两方面:

  • 启动SystemServer
  • 孵化应用进程

Android中大多数应用进程和系统进程都是通过Zygote进程来生成。Zygote为孵化的应用程序提供了几个基础资源:

  • 常用类 :Android的Java 类库, 大部分来自于 Apache Hamony, 开源的Java API 实现,如 java.lang, java.util, java.net
  • JNI函数
  • 主题资源 : 比如theme图片, 占用10M+的内存空间,这些内存是系统共享的
  • 共享库:

Zygote进程启动

Zygote由init进程解析init.rc脚本启动的。现在机器分为32位和64位,Zygote的启动也要区分对待:

代码语言:javascript
复制
/system/core/rootdir/init.rc
import /init.${ro.zygote}.rc

根据系统属性ro.zygote的具体值,加载不同的描述Zygote的rc脚本。譬如firely rk3399包含的文件: init.zygote32.rc init.zygote32_64.rc init.zygote64.rc init.zygote64_32.rc 其中zygote32和zygote64分别对应32位和64位机器。zygote32_64和zygote64_32则Primary Arch 和 Secondary Arch的组合,以init.zygote64.rc为例,相关脚本如下:

代码语言:javascript
复制
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

从路径来看,Zygote所在的应用程序名称叫"app_process64",

  • zygote 就是service的名称
  • /system/bin/app_process64 应用程序路径,即Zygote所在的应用进程
  • -Xzygote /system/bin --zygote --start-system-server 为传递给app_process的参数,后面的分析会看到参数--zygote被用来 app_process 启动Zygote的选项, --start-system-server 会被作为参数传递给Zygote的ZygoteInit.

可以简单地用下面这副图描述Zygote的启动

zygote创建.png

Zygote启动时都做了什么

Zygote启动主要经历了两部分:

  • native世界
  • java世界

native世界

从Zygote的rc脚本我们知道, Zygote是通过app_process启动,入口就是app_process的main函数 frameworks/base/cmds/app_process/app_main.cpp

代码语言:javascript
复制
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;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;
   
    ++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;
        }
    }

    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        
    }

这里有3点,

  1. 创建了AndroidRuntime对象, 这里面主要的动作就是启动虚拟机
  2. 解析传进来的参数 . 这个场景中--zygote 指定了app_process接下来将启动"ZygoteInit", 并传入-start-system-server
  3. 在虚拟机中运行ZygoteInit , ZygoteInit是java写的,即这一步Zygote就从native世界进入到了java世界

简单来说,Zygote在native世界做的主要是以下几步:

  • 启动Android虚拟机
  • 注册Android的JNI函数
  • 进入java世界

java世界

Zygote的java世界入口是ZygoteInit 的main函数 frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

代码语言:javascript
复制
 public static void main(String argv[]) {
            zygoteServer.registerServerSocket(socketName);   
            // In some configurations, we avoid preloading resources and classes eagerly.
            // In such cases, we will preload things prior to our first fork.
            if (!enableLazyPreload) {
                preload(bootTimingsTraceLog);  //预加载各类资源
            } else {
                Zygote.resetNicePriority();
            }

           if (startSystemServer) {
                Runnable r = forkSystemServer(abiList, socketName, zygoteServer);

                // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
                // child (system_server) process.
                if (r != null) {
                    r.run();
                    return;
                }
            }

            caller = zygoteServer.runSelectLoop(abiList);
}

ZygoteInit的主函数主要完成几件事情:

  • 注册一个socket Zygote 作为孵化器,跟其他进程间的通讯不是通过binder而是通过socket。一旦有新进程需要运行,系统会通过这个Socket(完整的名称为ANDROID_SOCKET_zygote)跟Zygote通讯,由zygote完成进程孵化过程
  • 预加载各类资源 函数preload用于加载虚拟机运行时所需的各类资源
  • 启动System Server
  • 进入Loop循环

其他问题点

为何用socket而不是binder

Zygote是通过fork来创建新进程的,而binder是多线程的,有可能造成死锁。

在 POSIX 标准中,fork 的行为是这样的:复制整个用户空间的数据(通常使用 copy-on-write 的策略,所以可以实现的速度很快)以及所有系统对象, 然后仅复制当前线程到子进程。这里:所有父进程中别的线程,到了子进程中都是突然蒸发掉的。

假如父进程在获取到锁的情况下,fork了一个子进程。子进程的内存中,这个锁的状态是上锁状态。子进程仅运行了fork所在的这个线程,其它线程没有运行,当它尝试获取锁时,就发生了死锁

为何要通过Zygote来孵化程序,而不是由其他进程直接创建

主要有两个好处:

  • 缩短应用的启动时间 无论哪个app,所有的虚拟机都必须按照某种一样的而且是确定的方式初始化。Zygote预加载了各种资源,创建了虚拟机。由Zygote创建的进程能够继承这些资源,不用再重新创建。加载app的类只是最后一步而已。
  • 优化共享内存 所有虚拟机都是从Zygote fork出来的,所以特么能够享受到由内核实现的内存共享的优势。比如Zygote预加载的各类资源,比如theme主题图片,所有的进程都是共享的,在物理内存中只需要保存一份。

Zygote这一设计有什么缺点

所有应用进程都是从同一个进程fork出来的,这会有效地破坏地址空间布局随机化,这一技术是对抗代码注入攻击的重要安全手段

参考 网络课程 --剖析Framework面试 冲击Android高级职位 最强Android书架构大剖析 深入理解Android内核设计思想 https://blog.csdn.net/boygrass/article/details/27103383

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022.03.15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Zygote是做什么的
  • Zygote进程启动
  • Zygote启动时都做了什么
    • native世界
      • java世界
      • 其他问题点
        • 为何用socket而不是binder
          • 为何要通过Zygote来孵化程序,而不是由其他进程直接创建
            • Zygote这一设计有什么缺点
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档