本文分析基于Android R

前言
一说到应用启动,估计大伙儿就会想到zygote进程。确实,正如其中文释义“受精卵”一样,其主要的作用就是孵化出一个又一个的应用进程。

传统的应用启动模式由system_server中的AMS接收请求,之后通过socket告知zygote,让其完成fork动作,这样新进程便创建出来。不过从Android Q(10)开始,Google引入了一种新的机制:USAP(Unspecialized App Process)。通过prefork的方式提前创建好一批进程,当有应用启动时,直接将已经创建好的进程分配给它,从而省去了fork的动作,因此可以提升性能。

这种机制在AOSP的源码中默认是关闭的,但估计很多手机厂家已经提前尝鲜了。

目录

1. USAP进程的工作流程

当开启USAP的功能后,zygote会维护一个进程池,其中最多可容纳10个USAP进程。以下是根据图示罗列的详细工作流程。

Zygote首先会fork出10个进程,将其加入到进程池中。被创建出来的USAP进程并不会执行具体逻辑,而是等待socket通信的到来。不过有一个细节,在等待socket通信之前,USAP进程会提升调度优先级,这样如果应用需要启动时,它能以最快的速度响应。

635  private static Runnable usapMain(LocalServerSocket usapPoolSocket,
636                                  FileDescriptor writePipe) {
637     final int pid = Process.myPid();
638     Process.setArgV0(Process.is64Bit() ? "usap64" : "usap32");
639
640     LocalSocket sessionSocket = null;
641     DataOutputStream usapOutputStream = null;
642     Credentials peerCredentials = null;
643     ZygoteArguments args = null;
644
645     // Change the priority to max before calling accept so we can respond to new specialization
646     // requests as quickly as possible.  This will be reverted to the default priority in the
647     // native specialization code.
648     boostUsapPriority();   <================= 提升进程调度优先级
649
650     while (true) {
651         try {
652             sessionSocket = usapPoolSocket.accept();

USAP进程等待的socket通信将会何时到来?这取决于应用进程何时启动。当AMS接收到启动需求时,其会调用Process.start()。当决定采用USAP的方式启动时,system_server便会发起socket通信,将所有启动参数发送过去。由于此时有10个USAP进程都在等待同一个socket端口,因此系统底层会随机唤醒一个进程来处理此次通信。

495  try (LocalSocket usapSessionSocket = zygoteState.getUsapSessionSocket()) {
496      final BufferedWriter usapWriter =
497              new BufferedWriter(
498                      new OutputStreamWriter(usapSessionSocket.getOutputStream()),
499                      Zygote.SOCKET_BUFFER_SIZE);
500      final DataInputStream usapReader =
501              new DataInputStream(usapSessionSocket.getInputStream());
502
503      usapWriter.write(msgStr);   <============== 发起socket请求
504      usapWriter.flush();
505
506      Process.ProcessStartResult result = new Process.ProcessStartResult();
507      result.pid = usapReader.readInt();   <=========== 等待USAP进程将自己的pid告知system_server

USAP进程被唤醒,调用specializeAppProcess来完成“腾笼换鸟”的工作。最终调入ActivityThread.main方法中去,进而完成应用的启动。
当该进程退出时,它不会回到USAP Pool中,而是直接被zygote回收。zygote接收到SIGCHLD信号后,会调用SigChldHandler进行处理。过程中会通过socket将回收的进程数告知zygote。目前这个信息未被使用,可能未来会有些新的设计。

2. USAP Pool的填充过程
随着启动的应用越来越多,USAP Pool中的进程将会不断消耗,因此便会涉及到重新填充(refill)的过程。

USAP Pool中设定了两个阈值,分别对应两种refill的方式。

2.1 Delayed Refill
每当USAP进程被使用时,它都会通过pipe将自己的pid告知zygote。这样zygote就可以将它从pool中删去。删去之后,zygote会去检查pool中剩余(空闲)进程的数量。当数量不超过一半(5)时,便会发起一次refill事件来fork出新的进程填充到pool中去。

真实的refill动作是滞后3秒的,这样可以和应用启动过程间隔开。因为当zygote接收到USAP进程的pid时,也意味着USAP进程正要执行specializeAppProcess,为了避免refill过程和应用启动过程抢夺系统资源(CPU资源)从而影响启动速度,原生设计中采用延时执行的方式。

731  try {
732      ByteArrayOutputStream buffer =
733              new ByteArrayOutputStream(Zygote.USAP_MANAGEMENT_MESSAGE_BYTES);
734      DataOutputStream outputStream = new DataOutputStream(buffer);
735
736      // This is written as a long so that the USAP reporting pipe and USAP pool event FD
737      // handlers in ZygoteServer.runSelectLoop can be unified.  These two cases should
738      // both send/receive 8 bytes.
739      outputStream.writeLong(pid);
740      outputStream.flush();
741
742      Os.write(writePipe, buffer.toByteArray(), 0, buffer.size());   <======= 往zygote发送pid
743  } catch (Exception ex) {
744      Log.e("USAP",
745              String.format("Failed to write PID (%d) to pipe (%d): %s",
746                      pid, writePipe.getInt$(), ex.getMessage()));
747      throw new RuntimeException(ex);
748  } finally {
749      IoUtils.closeQuietly(writePipe);
750  }
751
752  specializeAppProcess(args.mUid, args.mGid, args.mGids,     <================ 应用后续启动过程
753                       args.mRuntimeFlags, rlimits, args.mMountExternal,
754                       args.mSeInfo, args.mNiceName, args.mStartChildZygote,
755                       args.mInstructionSet, args.mAppDataDir, args.mIsTopApp,
756                       args.mPkgDataInfoList, args.mWhitelistedDataInfoList,
757                       args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs);

2.2 Immediate Refill
在Delay Refill的3秒内,系统有可能会有新的启动请求。当USAP Pool中的进程不断被消耗,以至于消耗殆尽时,这时便需要另一种机制,来保证USAP Pool的正常轮转。

当USAP Pool中最后一个进程被用掉后,zygote会发起一次immediate fork。由于这次fork在时间上和应用启动过程冲突,所以zygote只fork出一个进程,从而将影响降到最低。新fork出的进程会立即补充到pool中,这样接下来再有应用启动时,不会落入无USAP可用的境地。

此外,zygote会再安排一次Delayed Refill用于完整填充,多数情况下这一步没有必要(之前有一次Delayed Refill正在执行的路上),但加上它更加保险。

3. 如何开启/关闭
USAP的开启/关闭都通过property来实现。

一种方式是在build.prop中增加一行。persist.device_config.runtime_native.usap_pool_enabled=true

或者init.rc
on post-fs-data
setprop persist.device_config.runtime_native.usap_pool_enabled true
另一种方式是获取root权限后,调用setprop设置。

setprop persist.device_config.runtime_native.usap_pool_enabled true

开启和关闭的过程都是动态的,但生效的时机较为有趣。只有等property修改完后再一次启动应用时,10个USAP进程才会创建出来。关闭的时机也一样,只不过zygote会给空闲的USAP进程发送SIGTERM信号来结束它的生命,同时清空pool。

4. 总结
总体而言,USAP的机制较为简单,只是源码中的socket/pipe名称容易混乱,罗列如下。

至于此项机制到底能带来多少性能提升,笔者尚未测试过。如果有手机厂家的伙伴掌握了一手测试数据,不妨在评论里分享下

Android Framework | 一种新型的应用启动机制:USAP相关推荐

  1. 【Android Framework系列】第2章 Binder机制大全

    1 Binder简介 1.1 什么是Binder   Binder是Android中主要的跨进程通信方式.Android系统中,每个应用程序是由Android的Activity,Service,Bro ...

  2. Android高级面试问题及答案(1)——Android Framework篇

    问题1.Android系统的启动过程? 主要分为几个部分: init进程启动 1.按下电源,启动引导程序 BootLoader,启动linux内核,init进程启动,所以init是android系统的 ...

  3. 专题总纲目录 Android Framework 总纲

    专题总纲说明: 本系列文章虽说是 Android 的知识体系专题,同时也是学习Android Framework 系统的一个思路,尤其是当我们对Android 框架层 一点都不了解的时候,但前提是要有 ...

  4. Android Framework 入门学习

    前言 众所皆知,Android系统是当前占据用户量最大的手机系统,国内流行的客户端开发无非是android 与 ios,即使作为一个普通用户,小伙伴们可曾想过这个给我们生活带来巨大变化的系统是如何为我 ...

  5. Android Framework 电源子系统(01)PowerManagerService启动分析

    该系列文章总纲链接:专题分纲目录 Android Framework 电源子系统 本章关键点总结 & 说明: 本章节主要关注➕ 以上思维导图即可.该章节 主要是 对 PMS 启动的分析,从sy ...

  6. Android 编程下帧动画在 Activity 启动时自动运行的几种方式

    Android 开发过程中部分 Activity 在显示的时候就要求给用户显示一个进度框来改善用户体验,比如:Activity 在启动的时候就去联网请求数据.读取数据库内容等.进度框显示样式如下,采用 ...

  7. android的四种启动模式,(转)彻底弄懂Activity四大启动模式

    原地址:https://blog..net/mynameishuangshuai/article/details/51491074 最近有几位朋友给我留言,让我谈一下对Activity启动模式的理解. ...

  8. 玩转Android Framework启动篇

    一.为什么要阅读源码 作为Android开发者如果真正的想提高技术,必须要知道底层是如何运作的这样才能开发出性能良好的APP. 阅读源码是抱着一颗学习的心态,学习Google的代码风格,设计思想. 二 ...

  9. 详解android framework中StateMachine(HSM层次状态机)的实现

    [转载文章出处:https://segmentfault.com/a/1190000020386485?utm_source=tag-newest 作者:emonn] [最近在看状态机相关的问题,看到 ...

最新文章

  1. 中国人长期“霸榜”GitHub,国外开发者发文控诉
  2. 线程同步——内核对象实现线程同步——等待函数
  3. vmboxcentos安装重启又要安装_Windows 10八月更新再遇尴尬:安装失败 或安装后随机重启...
  4. PAT乙级(Basic Level)练习题-写出这个数 (20)
  5. C#设计模式--简单工厂模式
  6. HTML文件撰写的注意事项有哪些,CSS及HTML 常见误区和注意事项(一)
  7. CI框架中pdo的使用方法
  8. Codeforces Round #564 (Div. 2)A
  9. K - Let the Flames Begin Gym - 101955K(约瑟夫环/2018icpc沈阳)
  10. Python刷题-6
  11. CCF201903-2 二十四点(100分)【表达式计算】
  12. web server linux,GitHub - markparticle/WebServer: C++ Linux WebServer服务器
  13. Deepracer 学了就能云驾驭赛车? Deepracer机器学习入门级干货分享!
  14. 搭建自已的聊天服务器Rocket.Chat
  15. 文件怎么复制到虚拟机中的linux系统吗,Windows下的文件如何复制到虚拟机的Linux中...
  16. android killer 反编译工具,androidkiller反编译软件使用与踩坑并解决的过程
  17. C#Unity3d怪物在一定范围内巡逻发现玩家自动跟踪攻击,血量低于30%自动向后跳3米并缓慢回血的代码和使用
  18. APS排产提高生产效率的五大妙招
  19. 活动 | 旷视「智见AI」SpringCamp圆满结营 (附回放链接)
  20. 【爱贝观察】无现金支付,美国排第一,中国排第几呢?

热门文章

  1. Linux 1: Cloud computing operating system management | Linux
  2. NO.1 20200402 ZSS
  3. php 禁止模拟手机,php防止模拟请求 - bengozhong的个人空间 - OSCHINA - 中文开源技术交流社区...
  4. 机器学习基础——python——文件读取之压缩包读取,rar格式
  5. ubuntu 安装KDE桌面
  6. 大数据学习的关键技术知识体系及学习建议
  7. Pixhawk解锁常见错误
  8. Andorid串口开发打印机
  9. 阿里云安全管家服务内容
  10. nodejs学习consolidate