应用进程创建二三事

前言

"Zygote进程创建并通过ZygoteInit类初始化内部功能时,创建了ZygoteServer对象."

"然后呢?"

"runSelectLoop开启Socket循环,等待并处理消息,如ActivityManagerService要求创建应用进程命令"

  • Zygote进程交给谁轮询啊,具体怎么轮询的呀
  • 为什么runSelectLoop里有两个循环
  • 怎么取出消息呀,为什么都是从peer.get拿到的ZygoteConnection去processCommand呀,为什么不是从别的判断执行啊
  • 那外部需要创建进程时发的报文是怎么发的,是什么内容呀
  • 进程创建后为什么能到了Application#onCreate了呀

一个个来!

ZygoteServer轮询

Zygote进程创建并通过ZygoteInit类初始化内部功能时,创建了ZygoteServer对象,由该对象的runSelectLoop开启Socket循环,监听请求

while里做了什么

1. 重置状态

一定时间间隔获取USAP池相关系统属性,重置USAP池重填动作为无操作.

2. poll数据结构准备

创建poll结构数组,大小根据是否启用USAP池决定.如果USAP池启用,还获取USAP管道文件描述符

3. 填充poll数据结构

将pollIndex作为递增的数组索引,用于填充pollFDs数组,每填充一个元素后递增。这确保了所有需要监听的套接字文件描述符被正确添加到 poll结构中

填充完所有套接字描述符后,其值被保存为usapPoolEventFDIndex。这是一个重要的分界点,它标记了USAP池事件文件描述符在pollFDs数组中的位置

当USAP池启用时,pollIndex继续用于添加USAP池事件描述符和USAP管道描述符到pollFDs数组.

4. 计算poll超时时间

  • 如果没有设置触发时间戳,则返回-1,代表无限期阻塞,直到有事件发生(如收到新连接请求)
  • 如果超过延迟时间,返回0,代表不阻塞,并标记延迟重填,
  • 如果时间戳异常(时钟回调),重新设置完整延迟时间
  • 否则计算剩余等待时间,代表最多阻塞指定的毫秒数

5. 执行系统poll调用

调用底层OS的poll函数,等待文件描述符就绪.使用计算好的超时值调用系统的poll函数,这控制了Zygote进程在select循环中的阻塞行为.

6. 处理poll结果

  • 返回值为0表示超时发生,则此时标记USAP池需要延迟重填
  • 否则从后向前遍历poll结构数组,并检查USAP池状态并决定是否重填

遍历poll结构数组

处理poll结果时,pollIndex变成了一个反向迭代器。--pollIndex 使索引从数组末尾开始向前遍历

  • 优先处理新添加的事件:在数组后面的通常是最近添加的 USAP 相关文件描述符,这些可能需要优先处理
  • 安全移除元素:在处理过程中可能需要从 socketFDs 和 peers 数组中移除元素,从后向前遍历使得在移除过程中不会影响到尚未处理的元素的索引
  • 分层处理逻辑:通过判断 pollIndex 与 usapPoolEventFDIndex 的关系,可以区分是套接字事件还是 USAP 相关事件

会跳过没有POLLIN事件的描述符

  • pollIndex==0,代表Zygote服务的Socket,会接受新连接并添加到peers和socketFDs列表
  • pollIndex < usapPoolEventFDIndex,接收从ZygoteServer来的Socket内容
  • 其他值则代表是读取USAP相关文件描述符事件
接收从ZygoteServer来的Socket内容里

处理来自客户端的命令

  • 如果是子进程(fork后),这里就触发了ZygoteConnection#processCommand,并返回应该运行的命令.
  • 如果仍在服务器进程,清理已关闭的连接
  • 处理异常情况
  • 重置子进程标志

检查USAP池状态并决定是否重填

如果读取了USAP相关事件,检查当前USAP池大小:

  • 如果低于最小值,标记需要立即重填
  • 如果低于阈值,设置延迟重填触发时间戳

7. 执行USAP池重填

如果需要重填USAP池,收集所有会话套接字的原始文件描述符,根据重填类型(立即或延迟)调用fillUsapPool

  • 如果返回命令(成为USAP子进程),则返回该命令
  • 如果是优先重填但未完成,设置延迟重填时间戳继续填充

ZygoteCommand#processCommand内容

循环内容核心就是ZygoteConnection的processCommand方法,取出Socket内容后fork子进程

fork以后:

  • 父进程调用handleParentProc,通过套接字发送回PID信息

  • 子进程调用handleChildProc方法进行初始化

场景触发,创建进程

当用户点击桌面应用图标,创建Intent,最终走到ContextImpl.startActivity,请求经ActivityTaskManagerService调用ActivitManagerService的startProcessLocked.

最终在方法里判断调用Process.start方法(用于与Zygote进程通信以请求创建一个新的应用进程).

和zygote通信阶段

Process.start其实最后就是ZygoteProcess.start方法.如图:

startViaZygote方法具体步骤包括:

1. 拼接命令和参数准备发送给 Zygote

指定了targetSdkVersion,appdata文件夹等参数

2. 检测开启合适的Zygote进程打开和Zygote进程的连接

根据ABI选择合适的Zygote进程(32位或64位),检查ABI兼容性并返回适当的ZygoteState,必要时连接到次要的Zygote服务

3.发送参数并获取结果

与Zygote进程实际通信的核心部分.

携带参数通过zygoteState交互,前面提到新建进程,即子进程从Zygote进程fork出来后,会将结果告知父进程,所以能通过zygoteInputStream的readInt获取到pid.而子进程执行后续内容,就会执行到RuntimeInit的applicationInit,这就开始执行到ActivityThread.main阶段.

ActivityThread.main阶段

1. 从ActivityThread到ActivityManagerService

ActivityThread#main方法内触发ActivityThread#attach方法,并传入ActivityThread的ApplicationThread字段给到ActivityManagerService,让ActivityManagerService能操作ApplicationThread.

2. 从ActivityManagerService到ApplicationThread

ActivityManagerService将结果告知给ActivityThread

3. 从ApplicationThread到ActivityThread

ActivityManagerService将结果告知给ActivityThread的(内部H类型字段,再通过消息机制转发调用)handleBindApplication方法,创建Application对象,并触发onCreate声明周期方法.

结论

  • ZygoteServer开启了循环,使用poll阻塞的形式监听文字描述符
  • 使用了USAP池进行优化
  • 一层循环是阻塞轮询,二层是倒序pollIndex接收从ZygoteServer来的Socket内容,就会调用到ZygoteConnection#processCommand,并返回command
  • 外部发送报文在ZygoteProcess#startViaZygote方法内完成
  • Runtime#application内触发ActivityThread#main方法跟attach方法,最终创建Application对象并执行onCreate生命周期方法.

引用

xrefandroid.com/android-15....

xrefandroid.com/android-15....

xrefandroid.com/android-15....

xrefandroid.com/android-15....

xrefandroid.com/android-15....

xrefandroid.com/android-15....

xrefandroid.com/android-15....

xrefandroid.com/android-15....

xrefandroid.com/android-15....

xrefandroid.com/android-15....

xrefandroid.com/android-15....

相关推荐
流浪汉kylin1 小时前
Android 图片选择器改系统
android
前行的小黑炭1 小时前
Android 上下位机开发:串口是什么,为什么android版本都比较低?粘包半包的原因以及处理思路,缓冲区处理,以及超时清空缓冲区....
android
移动开发者1号1 小时前
你知道Android中配置resourcePrefix的作用吗?
android
tangweiguo030519872 小时前
Android Compose 系统 Scope 的优化实践
android
我命由我123452 小时前
Android Cordova 开发 - Cordova 快速入门(Cordova 环境配置、Cordova 第一个应用程序)
android·开发语言·前端框架·android studio·h5·安卓·android-studio
DEVIL2 小时前
Flutter中各类Controller的本质
android·flutter
京东零售技术3 小时前
与智者同行:京东零售技术人的成长书单
源码阅读
Kongzue5 小时前
让DialogX的消息提示玩出花 - 自定义PopTip和 PopNotification的避让动画
android·java
顾林海5 小时前
深入解析 Android Native Hook
android·面试·性能优化