ZygoteServer循环
runSelectLoop() 是 Zygote 常驻父进程的消费者循环。它同时管理主 Zygote server socket、已建立的 session socket、USAP pool event fd 和 USAP reporting pipe;同一个 pollfd 数组中的索引决定事件属于哪一种 owner。本文只展开循环本身,不重复 SS010 的 ABI 选择和 SS005 的 init socket 包装。
1. 两张并行表
源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
ArrayList<ZygoteConnection> peers = new ArrayList<>();
socketFDs.add(mZygoteSocket.getFileDescriptor());
peers.add(null); // index 0 is the server socket.
mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;socketFDs 与 peers 使用同一索引:索引 0 是主监听 socket,peers[1...] 是由主 socket accept 出来的 session connection。这个并行关系让 pollIndex 可以直接取出对应 ZygoteConnection;删除连接时必须同步删除两张表,否则下一轮事件会把 fd 映射到错误 peer。
2. pollfd构造
if (mUsapPoolEnabled) {
usapPipeFDs = Zygote.getUsapPipeFDs();
pollFDs = new StructPollfd[
socketFDs.size() + 1 + usapPipeFDs.length];
} else {
pollFDs = new StructPollfd[socketFDs.size()];
}
int pollIndex = 0;
for (FileDescriptor socketFD : socketFDs) {
pollFDs[pollIndex] = new StructPollfd();
pollFDs[pollIndex].fd = socketFD;
pollFDs[pollIndex].events = (short) POLLIN;
++pollIndex;
}
final int usapPoolEventFDIndex = pollIndex;
if (mUsapPoolEnabled) {
pollFDs[pollIndex++].fd = mUsapPoolEventFD;
for (int usapPipeFD : usapPipeFDs) {
FileDescriptor managedFd = new FileDescriptor();
managedFd.setInt$(usapPipeFD);
pollFDs[pollIndex++].fd = managedFd;
}
}构造顺序是:主 socket/session socket → USAP event fd → USAP reporting pipes。源码注释要求 USAP event/pipe 先于 session/server 事件被处理,以保持 USAP accounting 准确;实现通过记录 usapPoolEventFDIndex 和倒序处理 pollIndex 实现分类,而不是依赖 socket 名称。
3. 超时与补池
if (mUsapPoolRefillTriggerTimestamp == INVALID_TIMESTAMP) {
pollTimeoutMs = -1;
} else {
long elapsed = System.currentTimeMillis()
- mUsapPoolRefillTriggerTimestamp;
if (elapsed >= mUsapPoolRefillDelayMs) {
pollTimeoutMs = 0;
mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
} else {
pollTimeoutMs = (int)
(mUsapPoolRefillDelayMs - elapsed);
}
}
pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);没有延迟 refill 时 poll(-1) 无限等待;有延迟时间戳时,poll 只等待到 refill deadline。deadline 到期后先设置 DELAYED,再用非阻塞 poll 检查当前 ready fd,避免 refill 与已经到达的连接事件产生错误顺序。系统时钟回拨时源码重新开始倒计时,因为 currentTimeMillis() 不是单调时钟。
4. Socket事件
while (--pollIndex >= 0) {
if ((pollFDs[pollIndex].revents & POLLIN) == 0) continue;
if (pollIndex == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
socketFDs.add(newPeer.getFileDescriptor());
} else if (pollIndex < usapPoolEventFDIndex) {
ZygoteConnection connection = peers.get(pollIndex);
boolean multipleForksOK = !isUsapPoolEnabled()
&& ZygoteHooks.isIndefiniteThreadSuspensionSafe();
Runnable command = connection.processCommand(
this, multipleForksOK);主 socket 可读表示有新客户端,acceptCommandPeer() 创建新 peer 并把 fd 追加到两张并行表。session fd 可读则进入 processCommand(),一次消费一个 spawn request;multipleForksOK 只有在 USAP 未启用且 ART 允许无限线程挂起时才打开,不能被解释为普通连接的永久多 fork 权限。
5. 父子返回
if (mIsForkChild) {
if (command == null) {
throw new IllegalStateException("command == null");
}
return command;
} else {
if (command != null) {
throw new IllegalStateException("command != null");
}
if (connection.isClosedByPeer()) {
connection.closeSocket();
peers.remove(pollIndex);
socketFDs.remove(pollIndex);
}
}fork 后的 child 在同一 Java 调用栈中返回 Runnable,由 ZygoteInit.main() 最后执行 caller.run();父 Zygote 不应从 processCommand() 得到 Runnable。父端检测 peer close 后同步关闭 connection 并从两张表移除。异常时父端关闭 peer 让客户端立即感知,子端则记录 post-fork exception 并终止,避免带着半初始化状态继续执行。
6. USAP事件
源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
long messagePayload;
byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES];
int readBytes = Os.read(
pollFDs[pollIndex].fd, buffer, 0, buffer.length);
if (readBytes != Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) {
Log.e(TAG, "Incomplete read from USAP management FD of size "
+ readBytes);
continue;
}
messagePayload = new DataInputStream(
new ByteArrayInputStream(buffer)).readLong();
if (pollIndex > usapPoolEventFDIndex) {
Zygote.removeUsapTableEntry((int) messagePayload);
}
usapPoolFDRead = true;event fd 的 payload 是移除数量,reporting pipe 的 payload 是刚 specialization 的 USAP PID。两者都必须读取固定字节数;短读只记录错误并跳过本轮。只有 reporting pipe(索引大于 event fd 索引)才调用 removeUsapTableEntry(pid)。
7. refill决策
if (usapPoolFDRead) {
int usapPoolCount = Zygote.getUsapPoolCount();
if (usapPoolCount < mUsapPoolSizeMin) {
mUsapPoolRefillAction = UsapPoolRefillAction.IMMEDIATE;
} else if (mUsapPoolSizeMax - usapPoolCount
>= mUsapPoolRefillThreshold) {
mUsapPoolRefillTriggerTimestamp =
System.currentTimeMillis();
}
}低于 min 立即补,低于 max 且缺口达到 threshold 则延迟补。fillUsapPool() 接收当前 session fd 列表,防止新 USAP 继承不应保留的连接;优先 refill 返回 null 时再安排 delayed refill。USAP 统计是 event/pipe 消费后的状态,不是 poll 返回次数。
8. 失败定位与导航
源码文件:
frameworks/base/core/java/com/android/internal/os/ZygoteServer.javaframeworks/base/core/java/com/android/internal/os/Zygote.java
# 输入循环实现,输出 fd 索引、poll 超时、连接和异常路径。
rg -n "runSelectLoop|pollFDs|usapPoolEventFDIndex|Os\.poll|acceptCommandPeer|processCommand" \
frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
# 输入 USAP 消息和池统计,输出 PID 移除、min/threshold 和 refill。
rg -n "USAP_MANAGEMENT_MESSAGE_BYTES|removeUsapTableEntry|getUsapPoolCount|mUsapPoolSizeMin|fillUsapPool" \
frameworks/base/core/java/com/android/internal/os/ZygoteServer.java \
frameworks/base/core/java/com/android/internal/os/Zygote.java看到 Zygote 停止接受请求时,先区分主 socket 无事件、session processCommand() 异常、poll 失败和 USAP 短读;看到 USAP 数量异常,再联读 event fd、reporting pipe、table entry 和 refill timestamp,不能只看 pool count。
下一篇将进入 Zygote 的安全限制与 specialization 参数,展开 SELinux、uid/gid、capability 和 seccomp;不会重复本文的 pollfd 和 USAP refill 算法。
