USAP池
USAP(Unspecialized App Process)把“fork 一个空白应用进程”和“把它特化成目标应用”拆成两个时间点。Zygote 空闲时预 fork 若干 USAP,保存在 native table;应用启动时 SystemServer 直接连接 USAP socket,USAP 校验参数、返回自己的 pid、通知父 Zygote 从池表移除,随后执行 uid/gid/SELinux/mount specialization 和 zygoteInit()。池数量降低后,ZygoteServer 根据 min/max/threshold 立即或延迟补池。
1. 池策略参数
源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
mUsapPoolSizeMax = Integer.min(
ZygoteConfig.getInt(
ZygoteConfig.USAP_POOL_SIZE_MAX,
ZygoteConfig.USAP_POOL_SIZE_MAX_DEFAULT),
ZygoteConfig.USAP_POOL_SIZE_MAX_LIMIT);
mUsapPoolSizeMin = Integer.max(
ZygoteConfig.getInt(
ZygoteConfig.USAP_POOL_SIZE_MIN,
ZygoteConfig.USAP_POOL_SIZE_MIN_DEFAULT),
ZygoteConfig.USAP_POOL_SIZE_MIN_LIMIT);
mUsapPoolRefillThreshold = Integer.min(
ZygoteConfig.getInt(
ZygoteConfig.USAP_POOL_REFILL_THRESHOLD,
ZygoteConfig.USAP_POOL_REFILL_THRESHOLD_DEFAULT),
mUsapPoolSizeMax);max/min/threshold/delay 可运行时调整,并限制在硬编码上下界。若 min >= max,源码恢复默认值并把 threshold 设置为 max/2。策略由 ZygoteServer 保存,不属于单个 USAP。
2. fillUsapPool
int usapPoolCount = Zygote.getUsapPoolCount();
int numUsapsToSpawn;
if (isPriorityRefill) {
numUsapsToSpawn =
mUsapPoolSizeMin - usapPoolCount;
} else {
numUsapsToSpawn =
mUsapPoolSizeMax - usapPoolCount;
}
ZygoteHooks.preFork();
while (--numUsapsToSpawn >= 0) {
Runnable caller = Zygote.forkUsap(
mUsapPoolSocket,
sessionSocketRawFDs,
isPriorityRefill);
if (caller != null) return caller;
}
ZygoteHooks.postForkCommon();priority refill 只补到 min,降低应用关键路径延迟;delayed refill 补到 max。fork 前统一停止 ART daemon;父 Zygote 完成批量 fork 后再 postForkCommon()。session fd 列表传给 native child 关闭,避免 USAP 继承其他启动连接。
3. forkUsap父子分流
源码文件:frameworks/base/core/java/com/android/internal/os/Zygote.java
FileDescriptor[] pipeFDs = Os.pipe2(O_CLOEXEC);
FileDescriptor readFD = pipeFDs[0];
FileDescriptor writeFD = pipeFDs[1];
int pid = nativeForkApp(
readFD.getInt$(), writeFD.getInt$(),
sessionSocketRawFDs,
false /*argsKnown*/, isPriorityFork);
if (pid == 0) {
IoUtils.closeQuietly(readFD);
return childMain(null, usapPoolSocket, writeFD);
} else if (pid == -1) {
return null;
} else {
IoUtils.closeQuietly(writeFD);
nativeAddUsapTableEntry(pid, readFD.getInt$());
return null;
}每个 USAP 都有 reporting pipe。父进程把 pid/readFD 加入 native table,子进程保留 writeFD 并进入 childMain() 等待 specialization。fork 失败返回 null,调用方仍继续处理剩余补池循环,日志/池数量用于发现缺口。
4. USAP等待命令
Process.setArgV0(
Process.is64Bit() ? "usap64" : "usap32");
boostUsapPriority();
while (true) {
sessionSocket = usapPoolSocket.accept();
blockSigTerm();
Credentials peer = sessionSocket.getPeerCredentials();
ZygoteCommandBuffer buffer =
new ZygoteCommandBuffer(sessionSocket);
args = ZygoteArguments.getInstance(buffer);
applyUidSecurityPolicy(args, peer);
validateUsapCommand(args);
break;
}空白进程改名为 usap32/64,并提高 accept 阶段优先级。收到请求后先阻塞 SIGTERM,避免父 Zygote清池时杀掉正在 specialization 的 USAP;再读取 peer credentials、解析参数和执行 USAP 专用命令白名单。失败会关闭当前 session、解除 SIGTERM 后继续 accept。
5. PID双通道
usapOutputStream.writeInt(pid);
ByteArrayOutputStream buffer =
new ByteArrayOutputStream(
Zygote.USAP_MANAGEMENT_MESSAGE_BYTES);
DataOutputStream output =
new DataOutputStream(buffer);
output.writeLong(pid);
output.flush();
Os.write(writePipe, buffer.toByteArray(),
0, buffer.size());session socket 的 int pid 返回给 ZygoteProcess.attemptUsapSendArgsAndGetResult();reporting pipe 的 long pid 返回父 ZygoteServer,用于从 native USAP table 移除该进程并触发 refill。两个通道服务不同消费者,不能只读一个就认为池记账完成。
6. 进程特化
specializeAppProcess(
args.mUid, args.mGid, args.mGids,
args.mRuntimeFlags, rlimits,
args.mMountExternal, args.mSeInfo,
args.mNiceName, args.mStartChildZygote,
args.mInstructionSet, args.mAppDataDir,
args.mIsTopApp, args.mPkgDataInfoList,
args.mAllowlistedDataInfoList,
args.mBindMountAppDataDirs,
args.mBindMountAppStorageDirs,
args.mBindMountSyspropOverrides);
Zygote.setAppProcessName(args, TAG);
return ZygoteInit.zygoteInit(
args.mTargetSdkVersion,
args.mDisabledCompatChanges,
args.mEnabledCompatChanges,
args.mRemainingArgs, null);真正的 uid/gid、SELinux、mount、runtime flags 在这里生效;随后设置应用进程名并进入 RuntimeInit/ActivityThread。finally 解除 SIGTERM,恢复普通进程信号行为。
7. refill状态机
USAP event fd 报告批量移除数量,reporting pipe 报告 specialization pid。ZygoteServer 消费后读取 getUsapPoolCount():低于 min 立即补到 min;低于 max 且缺口达到 threshold 时设置延迟 timestamp;poll timeout 到期后补到 max。状态变化和 refill 都由父 Zygote执行,不由 USAP 子进程自我复制。
8. 失败与回退
- USAP 不支持/未启用/policy 不允许:客户端直接走普通 Zygote;
- USAP socket I/O 失败:ZygoteProcess 记录后回退普通 Zygote;
- 命令不在 USAP 白名单:USAP 关闭 session 并继续等待;
- reporting pipe 写失败:子进程终止,父池最终通过 signal/event 清理;
- specialization 失败:客户端得到负 pid/异常;
- pool policy 无效:恢复默认 min/max/threshold;
- 清池期间 specialization:SIGTERM 屏蔽防止竞态,完成后恢复。
9. 导航与后续
# ZygoteServer 的策略、补池和 event/report pipe 消费。
rg -n "USAP_POOL_SIZE|fetchUsapPoolPolicyProps|fillUsapPool|getUsapPoolCount|IMMEDIATE|DELAYED|usapPoolEventFDIndex" \
frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
# USAP fork、native table、accept、PID 回报和 specialization。
rg -n "forkUsap|nativeAddUsapTableEntry|childMain|boostUsapPriority|blockSigTerm|writeLong|specializeAppProcess|removeUsapTableEntry" \
frameworks/base/core/java/com/android/internal/os/Zygote.java
# 客户端 eligibility、USAP 请求和普通 Zygote 回退。
rg -n "shouldAttemptUsapLaunch|attemptUsapSendArgsAndGetResult|attemptZygoteSendArgsAndGetResult" \
frameworks/base/core/java/android/os/ZygoteProcess.java下一篇将分析 Linux fork/COW 与 Zygote 预加载之间的真实内存关系;不会重复 USAP 池协议和 refill。
