Android Framework 的跨进程调用几乎处处可见 Binder:AMS、PMS、WMS 都通过 Binder 暴露能力。普通 App 又通常由 AMS 侧发起创建。于是一个自然问题出现了:
Android 实际怎样把创建请求交给 Zygote?我们又能否从这份实现反推出"不选 Binder"的唯一原因?
先把最容易说错的结论摆在前面:
源码可以直接证明:Android 使用 init 预建的 Unix domain socket,
ZygoteServer接收请求,Zygote/USAP 执行 fork 与 specialize。源码不能单独证明:Google 只因为某一个原因没有选择 Binder。Binder 完全可以承载"请创建进程"这类命令;启动依赖、fork 线程模型、FD、安全与长期兼容性只能作为有证据支撑的架构解释,不能包装成唯一官方因果。
本文追一条普通 App 进程创建链:
scss
AMS / ProcessList
→ Process.start()
→ ZygoteProcess.start()
→ LocalSocket 写请求
→ ZygoteServer.runSelectLoop()
→ ZygoteConnection.processCommand()
→ forkAndSpecialize()
→ child → ActivityThread.main()
Activity 组件调度、Application 创建与首帧不在本文展开。
源码基线: Android 16 QPR2
android-16.0.0_r4,frameworks/basecommit45034f0663f960d9ee5fb0a101a4732b71f6e2f4。
一、先分清普通 App 创建中的两个角色
普通 App 的创建请求通常源于 system_server,但"system_server 发起请求"不等于"system_server 自己 fork App"。
| 阶段 | 谁做决定 | 谁执行 fork |
|---|---|---|
| AMS 判断需要目标进程 | system_server 中的 ActivityManagerService / ProcessList | 还没有 fork |
| 组装 UID、GID、runtime flags 等 | system_server 中的 ProcessList / ZygoteProcess | 还没有 fork |
| 把请求写入 Zygote socket | system_server | 还没有 fork |
forkAndSpecialize() |
Zygote / USAP | Zygote / USAP 进程执行 fork |
| 管理新 PID 与进程记录 | system_server | 读取结果后纳入管理 |
这条边界很重要。system_server 是控制者和进程记录管理者,Zygote 是实际孵化者。
二、客户端:从 ProcessList 到 ZygoteProcess
以常规应用进程为例,ProcessList.startProcessLocked() 会结合 ApplicationInfo、HostingRecord、隔离策略和已有记录,决定是否需要新进程。
关键调用逐层收敛:
scss
ProcessList.startProcessLocked(...)
→ ProcessList.startProcess(...)
→ Process.start(...)
→ ZYGOTE_PROCESS.start(...)
→ ZygoteProcess.startViaZygote(...)
普通应用的入口类通常由 ProcessList 指定为:
android.app.ActivityThread
这只是进程主类,不代表 Activity 已经创建,更不代表 Launcher 已经显示。
三、startViaZygote() 发送了哪些信息?
ZygoteProcess.startViaZygote() 把强类型参数编码为 Zygote 能解析的命令行式参数。典型维度包括:
运行时标志与目标 SDK、UID/GID 和附加组、进程名、SELinux seInfo、ABI、数据目录、包名、存储挂载模式,以及最终入口类 android.app.ActivityThread。具体参数会随版本和进程类型变化,主线只需确认:system_server 描述目标身份,Zygote 负责校验并执行 specialize。
这不是随意把 shell 命令交给 root 进程。服务端会重新解析、校验调用者身份与允许参数,并在 Native specialize 中落地权限。客户端提供目标信息,不代表客户端可以越权指定任意 UID/capability。
四、协议长什么样?

Android 16 的直接 socket 路径在 zygoteSendArgsAndGetResult() 中先写参数个数,再逐项写入以换行结尾的参数;客户端预先拒绝参数内换行,避免破坏 framing。服务端响应至少返回新 PID 与 wrapper 状态。
这是一种 Android 内部本地协议,不是对第三方 App 开放的公共 API。实现可以随平台版本演进,调用方应走 Framework 的进程管理接口,而不是自行伪造 Zygote 消息。
五、socket 从哪里来?不是 Zygote 随便 bind 的普通端口
init 的 Zygote Service 配置包含:
perl
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
init 在 fork/exec 前创建 socket,设置权限/上下文,并把对应文件描述符交给 app_process/Zygote。Zygote Java 侧根据 socket 名接管服务端端点。
这条启动设计带来清晰的所有权链:
csharp
init 配置并创建受控端点
→ Zygote 继承 server FD
→ system_server 以允许身份连接
→ Zygote 验证 peer credentials 与命令参数
它不是 TCP 网络服务;通信限定在本机 Unix domain socket 与 Android 权限模型内。
六、服务端:一个 select loop 怎样同时接收连接和处理结果?
Zygote 父进程完成 system_server fork 后进入 ZygoteServer.runSelectLoop()。核心模型是:
scss
poll server socket + 已建立连接
↓
server socket 可读:accept,创建 ZygoteConnection
↓
client connection 可读:processCommand()
↓
解析参数、执行安全检查
↓
forkAndSpecialize()
在 ZygoteConnection.processCommand() 中,服务端解析 ZygoteArguments,校验调用者和参数,整理 fork 前后的文件描述符并调用 forkAndSpecialize();父分支向客户端报告 PID,子分支关闭服务端资源并返回后续 Runnable。
父分支继续承担 Zygote;子分支进入 RuntimeInit.zygoteInit(),最后运行 ActivityThread.main()。
七、从源码事实出发,可以提出哪些架构解释?
这里先列源码直接事实:
perl
Zygote 的普通创建入口确实是 LocalSocket;
ZygoteServer 使用自己的 poll/select loop;
请求在 ZygoteConnection 中串行解析并进入 fork;
ForkCommon 明确要求 fork 点处于受控单线程状态;
Zygote 父进程本身不按普通服务方式启动 Binder 线程池;
Binder 线程池是在 fork 后 child 的 nativeZygoteInit 路径启动。
基于这些事实,可以做出谨慎的工程解释。下面三点回答的是"当前方案为什么合理",不是"官方只因这三点排除了 Binder"。
1. fork 希望运行在可预测的线程模型中
Binder 的常见服务模型是线程池并发分发 transaction。如果 Zygote 作为普通 Binder 服务,必须额外保证:
perl
多个 Binder 请求绝不并发 fork;
Binder 工作线程、驱动映射和运行时锁在 fork 前后都被正确协调;
子进程不继承不该保留的 Binder 服务端状态;
回调、死亡通知和排队 transaction 不破坏 fork 环境。
理论上可以专门设计一个单线程 Binder 协议来解决这些问题,但 Android 现有实现选择了更直接的 Zygote 自有 socket loop。源码证明的是"选择了 socket 和受控循环";"线程模型更简单"是由实现约束支撑的设计解释,不是源码中的唯一官方因果声明。
2. socket 很适合早期、点对点的进程工厂协议
Zygote 不需要成为通用服务发现目录中的能力对象。init 已经为它创建固定端点,system_server 知道主/次 Zygote 地址;请求是点对点的命令/响应,并需要处理连接凭据和文件描述符。
这使启动依赖保持为:
perl
init socket → Zygote loop → fork
而无需引入"发布 Binder 接口、注册到 ServiceManager、建立 Binder 服务分发模型"这组额外生命周期。
3. Unix peer credentials 提供服务端安全输入
Local socket 能提供对端 UID/GID/PID 等凭据。ZygoteConnection 使用 peer credentials 对敏感参数施加策略,再配合 socket 文件权限、SELinux 与 Native specialize 检查。
这不是说 Binder 没有调用者身份;Binder 同样提供 calling UID/PID。结论只是:当前 socket 方案已经提供 Zygote 协议所需的本地身份与 FD 传递能力。
八、三种错误理由为什么站不住?
错误 1:Binder 不能创建进程
Binder 是 IPC 传输与对象调用机制。它完全可以把"请创建进程"的参数交给一个已有服务;真正调用 fork() 的仍然是服务端进程。
所以应该说:
Android 的 Zygote 实现没有用 Binder 接收普通 App 创建请求;
而不是:
Binder 从原理上无法承载进程创建请求。
错误 2:因为开机时 Binder/servicemanager 一定还不存在
普通 App 的运行期创建发生在 system_server 及大量 Binder 基础设施已经运行之后。仅用"启动太早"解释整个长期协议并不成立。
主 Zygote 确实在早期初始化,但选择 socket 的源代码边界更直接:端点由 init 准备,Zygote 自己进入 socket loop,父进程没有按普通 Java Binder 服务建立接口和线程池。
错误 3:socket 天然比 Binder 更安全
两者安全性都依赖完整设计:端点权限、SELinux、身份校验、参数校验、服务端实现和最小权限。使用 Unix socket 并不会自动消灭越权风险。
准确说法是:Zygote socket 配置、peer credentials、服务端策略与 specialize 共同构成当前安全边界。
九、USAP 如何插入这条链?
ZygoteProcess 在满足配置与参数条件时,可以把请求发给 USAP pool socket,由已预 fork 的 unspecialized process 读取具体参数、完成 specialize 后进入 App。
若 USAP 失败或请求不适合,客户端会回退到主 Zygote 直接 socket 路径。它改变的是"fork 发生在请求前还是请求中",不改变 AMS 负责决策、Zygote 体系负责孵化、child 负责 specialize 的边界。
十、socket 返回 PID,不代表 App 已经可用
客户端读到 PID 只能证明 Zygote 父分支创建了子进程并返回结果。后面仍可能发生:
arduino
child specialize 失败;
RuntimeInit / ActivityThread.main() 异常;
绑定 Application 超时;
进程在 attach 到 AMS 前退出;
组件启动或首帧失败。
ProcessList 还要校验和维护 ProcessRecord、启动序号、PID 映射,并等待新进程通过 Binder 向 AMS attach。不要把 Zygote 响应与"应用启动完成"画成同一个点。
十一、把事实、解释和不能证明的结论放在同一张表里
| 说法 | 类型 | 可信边界 |
|---|---|---|
| Android 16 通过 LocalSocket 向 Zygote 发普通进程创建请求 | 源码事实 | ZygoteProcess 可直接证明 |
服务端由 ZygoteServer poll 并由 ZygoteConnection 解析 |
源码事实 | 固定调用链可直接证明 |
| fork 发生在 Zygote/USAP,不在 system_server | 源码事实 | Native fork 位置可直接证明 |
| socket 方案有助于维持受控 fork 线程模型 | 工程解释 | 与单线程 fork、无父 Zygote Binder pool 的实现一致 |
| Binder 从原理上绝对不能用于 Zygote | 错误绝对化 | Binder 能传命令,系统只是没有这样实现 |
| Google 选择 socket 只有一个唯一原因 | 不能证明 | 设计通常是启动依赖、线程、FD、安全与兼容性的综合结果 |
十二、回到标题:我们最终能确认什么?
system_server 通过
ZygoteProcess把目标进程参数写入 init 预建的 Unix domain socket;Zygote 在自有的受控循环中验证请求、fork 并 specialize,父分支返回 PID,子分支进入ActivityThread.main()。选择 socket 是 Android 的具体进程工厂设计,不代表 Binder 在原理上不能传递创建请求。
到这里,普通 App 的运行期出生路径已经闭合;但 system_server 并不是通过这条 socket 请求创建的。下一篇回到那个已经由 Zygote 直接 fork 出来的子进程:它进入 SystemServer.main() 时已经拥有什么,又必须补齐哪些容器设施?
源码定位
ProcessList.startProcessLocked():AMS 侧进程决策与入口类。Process.start():Framework 到ZYGOTE_PROCESS。ZygoteProcess.startViaZygote():请求参数与 socket 发送。ZygoteServer.runSelectLoop():服务端 poll/accept 循环。ZygoteConnection.processCommand():解析、安全检查与 fork 分支。- AOSP Zygote 官方说明:Zygote socket、USAP 与应用进程创建概览。