Zygote 孵化流程¶
相关链接
本文暂未考虑 fork child zygote 的情况
1. ZygoteInit 初始化¶
ZygoteInit.java 在 /frameworks/base/core/java/com/android/internal/os/
ZygoteInit 的主要流程是预加载资源,如果参数里有 --start-system-server 则孵化 SystemServer,最后启动 runSelectLoop 进入循环等待的创建请求。
ZygoteInit 的 main 启动中会判断了传入参数有没有 --start-system-server,如果有则会孵化 SystemServer 进程。
2. Zygote 监听处理其他进程的 fork 请求¶
当别的应用需要孵化一个新的进程时,会通过本地 socket 向 Zygote 发送请求,Zygote 收到请求后会执行 fork 来创建新的进程。
具体流程如下:
ZygoteServer监听请求
上文提到ZygoteInit启动了runSelectLoop方法负责监听和管理请求,代码中可以看出将具体的处理工作交给ZygoteConnection。上面通过/frameworks/base/core/java/com/android/internal/os/ZygoteServer.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
// line 388:434 /** * Runs the zygote process's select loop. Accepts new connections as * they happen, and reads commands from connections one spawn-request's * worth at a time. * @param abiList list of ABIs supported by this zygote. */ Runnable runSelectLoop(String abiList) { ArrayList<FileDescriptor> socketFDs = new ArrayList<>(); ArrayList<ZygoteConnection> peers = new ArrayList<>(); socketFDs.add(mZygoteSocket.getFileDescriptor()); peers.add(null); mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP; while (true) { fetchUsapPoolPolicyPropsWithMinInterval(); mUsapPoolRefillAction = UsapPoolRefillAction.NONE; int[] usapPipeFDs = null; StructPollfd[] pollFDs; // Allocate enough space for the poll structs, taking into account // the state of the USAP pool for this Zygote (could be a // regular Zygote, a WebView Zygote, or an AppZygote). if (mUsapPoolEnabled) { usapPipeFDs = Zygote.getUsapPipeFDs(); pollFDs = new StructPollfd[socketFDs.size() + 1 + usapPipeFDs.length]; } else { pollFDs = new StructPollfd[socketFDs.size()]; } /* * For reasons of correctness the USAP pool pipe and event FDs * must be processed before the session and server sockets. This * is to ensure that the USAP pool accounting information is * accurate when handling other requests like API deny list * exemptions. */ int pollIndex = 0; for (FileDescriptor socketFD : socketFDs) { pollFDs[pollIndex] = new StructPollfd(); pollFDs[pollIndex].fd = socketFD; pollFDs[pollIndex].events = (short) POLLIN; ++pollIndex; } // ... // line 485:548 try { pollReturnValue = Os.poll(pollFDs, pollTimeoutMs); } catch (ErrnoException ex) { throw new RuntimeException("poll failed", ex); } if (pollReturnValue == 0) { // The poll returned zero results either when the timeout value has been exceeded // or when a non-blocking poll is issued and no FDs are ready. In either case it // is time to refill the pool. This will result in a duplicate assignment when // the non-blocking poll returns zero results, but it avoids an additional // conditional in the else branch. mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP; mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED; } else { boolean usapPoolFDRead = false; while (--pollIndex >= 0) { if ((pollFDs[pollIndex].revents & POLLIN) == 0) { continue; } if (pollIndex == 0) { // Zygote server socket ZygoteConnection newPeer = acceptCommandPeer(abiList); peers.add(newPeer); socketFDs.add(newPeer.getFileDescriptor()); } else if (pollIndex < usapPoolEventFDIndex) { // Session socket accepted from the Zygote server socket try { ZygoteConnection connection = peers.get(pollIndex); boolean multipleForksOK = !isUsapPoolEnabled() && ZygoteHooks.isIndefiniteThreadSuspensionSafe(); final Runnable command = connection.processCommand(this, multipleForksOK); // TODO (chriswailes): Is this extra check necessary? if (mIsForkChild) { // We're in the child. We should always have a command to run at // this stage if processCommand hasn't called "exec". if (command == null) { throw new IllegalStateException("command == null"); } return command; } else { // We're in the server - we should never have any commands to run. if (command != null) { throw new IllegalStateException("command != null"); } // We don't know whether the remote side of the socket was closed or // not until we attempt to read from it from processCommand. This // shows up as a regular POLLIN event in our regular processing // loop. if (connection.isClosedByPeer()) { connection.closeSocket(); peers.remove(pollIndex); socketFDs.remove(pollIndex); } } } catch (Exception e) {Os.poll来监听套接字的事件,当有新的连接时,创建一个ZygoteConnection来处理这个连接。-
ZygoteConnection处理连接
ZygoteConnection负责解析请求参数,并调用Zygote.forkAndSpecialize来执行fork操作。 -
Zygote执行 Fork
forkAndSpecialize方法会调用native方法nativeForkAndSpecialize来真正执行fork。我们可以对比一下孵化普通应用进程 (
forkAndSpecialize) 和孵化SystemServer(forkSystemServer) 的代码,它们最终都依赖native方法。
3. Native 层:执行 Fork¶
Java 层的 native 方法最终会调用到 C++ 层的 ForkCommon 函数来完成进程的创建。
可以看到,它们都调用了 ForkCommon 方法。
我们来看 ForkCommon 的实现方法:
ForkCommon 调用了底层的 fork() 方法,并且接受了返回的 pid。
4. Fork 完成到应用进程启动¶
上文中在 fork 完成后,在子进程中会走进 handleChildProc 或 handleSystemServerProcess 我们来看这两个方法
可以看出它们都会先设置 1. 进程的名字
而 system server 还会额外设置
1. 设置 umask,限制 system_server 创建文件时的默认权限
2. 处理 SYSTEMSERVERCLASSPATH 提前准备自己的 classpath
3. 创建 system_server 使用的 ClassLoader 用于加载 SYSTEMSERVERCLASSPATH 中的 jar
4. 处理 system server / boot classpath profile,用于 profile 采集和启动优化
最后它们都会进入 ZygoteInit.zygoteInit
在这里,可以看出先走进了 jni 方法 nativeZygoteInit 通过调用当前 AndroidRuntime 对象的 onZygoteInit()启动了 binder 线程池
| /frameworks/base/core/jni/AndroidRuntime.cpp | |
|---|---|
调用了当前 AndroidRuntime 对象的 onZygoteInit(),这实际是一个虚函数,在 frameworks/base/cmds/app_process/app_main.cpp 中实现了这个函数:
| /frameworks/base/cmds/app_process/app_main.cpp | |
|---|---|
执行完后,进入 RuntimeInit.zygoteInit 来启动应用的 main 方法
zygoteInit 先配置 sdk 等环境,然后调用 findStaticMain
通过 ClassLoader 加载目标类,然后反射找到它的 public static main(String[] args)
这里将目标类的 main 方法保存到 Runnable MethodAndArgsCaller 中了,并且在 run 中能通过 Method.invoke 来调用这个 main 方法
这里的 Runnable 会一直返回到开头的 ZygoteInit 的 main 中调用 run 方法来启动