美文网首页
Android Framework | 一种新型的应用启动机制:

Android Framework | 一种新型的应用启动机制:

作者: 蜗牛是不是牛 | 来源:发表于2023-01-10 14:15 被阅读0次

    本文分析基于Android R

    前言

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

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

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

    目录

    image

    1. USAP进程的工作流程

    image

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

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

    [frameworks/base/core/java/com/android/internal/os/Zygote.java]

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

    [frameworks/base/core/java/android/os/ZygoteProcess.java]

    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
    
    
    1. USAP进程被唤醒,调用specializeAppProcess来完成“腾笼换鸟”的工作。最终调入ActivityThread.main方法中去,进而完成应用的启动。

    当该进程退出时,它不会回到USAP Pool中,而是直接被zygote回收。zygote接收到SIGCHLD信号后,会调用SigChldHandler进行处理。过程中会通过socket将回收的进程数告知system_server。目前这个信息未被使用,可能未来会有些新的设计。

    2. USAP Pool的填充过程

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

    image

    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资源)从而影响启动速度,原生设计中采用延时执行的方式。

    [frameworks/base/core/java/android/os/ZygoteProcess.java]

    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
    
    

    另一种方式是获取root权限后,调用setprop设置。

    setprop persist.device_config.runtime_native.usap_pool_enabled true
    
    

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

    4. 总结

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

    名称 作用
    名为"usap_pool_primary"的socket system_server通过它将启动参数发送给USAP进程
    gUsapPoolEventFD
    ZygoteServer.mUsapPoolEventFD SigChldHandler通过它将已退出的进程数发送给zygote进程
    (gUsapTable中元素的)read_pipe_fd USAP进程通过它将自身pid发送给zygote进程
    名为"zygote"的socket system_server通过它将命令参数发送给zygote进程

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

    本文转自 https://juejin.cn/post/6922704248195153927,如有侵权,请联系删除。

    相关文章

      网友评论

          本文标题:Android Framework | 一种新型的应用启动机制:

          本文链接:https://www.haomeiwen.com/subject/ztdvcdtx.html