从Android Q(10)开始,Google引入了一种新的机制,加快了app的启动时间,具体请看Android Framework | 一种新型的应用启动机制:USAP,本篇将会详细介绍USAP 进程启动的流程。
从Activity启动流程 上篇(Android 10),我们得知在Activity启动过程中,我们会调用到\frameworks\base\core\java\android\os\ZygoteProcess.java
的start
方法,然后调用startViaZygote()
,其实在调用startViaZygote()
之前还有一步:
public final Process.ProcessStartResult start(@NonNull final String processClass,
final String niceName,
int uid, int gid, @Nullable int[] gids,
int runtimeFlags, int mountExternal,
int targetSdkVersion,
@Nullable String seInfo,
@NonNull String abi,
@Nullable String instructionSet,
@Nullable String appDataDir,
@Nullable String invokeWith,
@Nullable String packageName,
boolean useUsapPool,
@Nullable String[] zygoteArgs) {
// TODO (chriswailes): Is there a better place to check this value?
//---------------------------- 就是这里 ----------------------------
if (fetchUsapPoolEnabledPropWithMinInterval()) {
informZygotesOfUsapPoolStatus();
}
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
packageName, useUsapPool, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
从函数名称,我们可以得知,这里用fetchUsapPoolEnabledPropWithMinInterval
判断系统是否开启了USAP功能,如果开启则调用informZygotesOfUsapPoolStatus()
:
/**
* Sends messages to the zygotes telling them to change the status of their USAP pools. If
* this notification fails the ZygoteProcess will fall back to the previous behavior.
*/
private void informZygotesOfUsapPoolStatus() {
final String command = "1\n--usap-pool-enabled=" + mUsapPoolEnabled + "\n";
synchronized (mLock) {
try {
attemptConnectionToPrimaryZygote();
primaryZygoteState.mZygoteOutputWriter.write(command);
primaryZygoteState.mZygoteOutputWriter.flush();
} catch (IOException ioe) {
mUsapPoolEnabled = !mUsapPoolEnabled;
Log.w(LOG_TAG, "Failed to inform zygotes of USAP pool status: "
+ ioe.getMessage());
return;
}
if (mZygoteSecondarySocketAddress != null) {
try {
attemptConnectionToSecondaryZygote();
try {
secondaryZygoteState.mZygoteOutputWriter.write(command);
secondaryZygoteState.mZygoteOutputWriter.flush();
// Wait for the secondary Zygote to finish its work.
secondaryZygoteState.mZygoteInputStream.readInt();
} catch (IOException ioe) {
throw new IllegalStateException(
"USAP pool state change cause an irrecoverable error",
ioe);
}
} catch (IOException ioe) {
// No secondary zygote present. This is expected on some devices.
}
}
// Wait for the response from the primary zygote here so the primary/secondary zygotes
// can work concurrently.
try {
// Wait for the primary zygote to finish its work.
primaryZygoteState.mZygoteInputStream.readInt();
} catch (IOException ioe) {
throw new IllegalStateException(
"USAP pool state change cause an irrecoverable error",
ioe);
}
}
}
可以看到该函数将调用attemptConnectionToPrimaryZygote
或attemptConnectionToSecondaryZygote()
这其实和Zygote()运行的位数有关,32或者64位,他们都调用了ZygoteState.connect
,只是传入的参数不同,我们看attemptConnectionToPrimaryZygote
:
private void attemptConnectionToPrimaryZygote() throws IOException {
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
primaryZygoteState =
ZygoteState.connect(mZygoteSocketAddress, mUsapPoolSocketAddress);
maybeSetApiBlacklistExemptions(primaryZygoteState, false);
maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
maybeSetHiddenApiAccessStatslogSampleRate(primaryZygoteState);
}
}
然后看ZygoteState.connect
:
static ZygoteState connect(@NonNull LocalSocketAddress zygoteSocketAddress,
@Nullable LocalSocketAddress usapSocketAddress)
throws IOException {
DataInputStream zygoteInputStream;
BufferedWriter zygoteOutputWriter;
final LocalSocket zygoteSessionSocket = new LocalSocket();
if (zygoteSocketAddress == null) {
throw new IllegalArgumentException("zygoteSocketAddress can't be null");
}
try {
zygoteSessionSocket.connect(zygoteSocketAddress);
zygoteInputStream = new DataInputStream(zygoteSessionSocket.getInputStream());
zygoteOutputWriter =
new BufferedWriter(
new OutputStreamWriter(zygoteSessionSocket.getOutputStream()),
Zygote.SOCKET_BUFFER_SIZE);
} catch (IOException ex) {
try {
zygoteSessionSocket.close();
} catch (IOException ignore) { }
throw ex;
}
return new ZygoteState(zygoteSocketAddress, usapSocketAddress,
zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter,
getAbiList(zygoteOutputWriter, zygoteInputStream));
}
这段代码比较好理解,就是创建一个LocalSocket
和Zygote
建立连接,并获取输入输出流设置到ZygoteState
中,待会儿我们会用到,至此attemptConnectionToPrimaryZygote
调用完成,回到上面的informZygotesOfUsapPoolStatus
,代码将command
写给了Zygote,而command
为"1\n--usap-pool-enabled=" + mUsapPoolEnabled + "\n"
,这里mUsapPoolEnabled
自然为true
, 记住这个command
,接下来可以到zygote
进程中查看了。
在Android系统启动流程末尾,我们说到Zygote进程会执行zygoteServer.runSelectLoop(abiList)
,接收并处理AMS传过来的消息,比如fork app进程。这里我们直接看runSelectLoop
函数即可:
Runnable runSelectLoop(String abiList) {
while (true) {
....
try {
Os.poll(pollFDs, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
....
....
ZygoteConnection connection = peers.get(pollIndex);
final Runnable command = connection.processOneCommand(this);
}
}
这里runSelectLoop
会使用epoll
机制,阻塞在Os.poll(pollFDs, -1)
,获取对方连接请求后,执行\frameworks\base\core\java\com\android\internal\os\ZygoteConnection.java
的processOneCommand
方法:
Runnable processOneCommand(ZygoteServer zygoteServer) {
....
parsedArgs = new ZygoteArguments(args);
....
if (parsedArgs.mUsapPoolStatusSpecified) {
return handleUsapPoolStatusChange(zygoteServer, parsedArgs.mUsapPoolEnabled);
}
....
ZygoteArguments
有代码如下:
....
....
} else if (arg.startsWith("--usap-pool-enabled=")) {
mUsapPoolStatusSpecified = true;
mUsapPoolEnabled = Boolean.parseBoolean(arg.substring(arg.indexOf('=') + 1));
expectRuntimeArgs = false;
} else {
break;
}
....
还记的我们传入的参数是什么吗?"1\n--usap-pool-enabled=" + mUsapPoolEnabled + "\n"
,这里就将mUsapPoolStatusSpecified
设置为true
,mUsapPoolEnabled
设置为 mUsapPoolEnabled
也为true
。所以会执行
handleUsapPoolStatusChange(zygoteServer, parsedArgs.mUsapPoolEnabled)
=> zygoteServer.setUsapPoolStatus
:
Runnable setUsapPoolStatus(boolean newStatus, LocalSocket sessionSocket) {
if (!mUsapPoolSupported) {
Log.w(TAG,
"Attempting to enable a USAP pool for a Zygote that doesn't support it.");
return null;
} else if (mUsapPoolEnabled == newStatus) {
return null;
}
Log.i(TAG, "USAP Pool status change: " + (newStatus ? "ENABLED" : "DISABLED"));
mUsapPoolEnabled = newStatus;
if (newStatus) {
return fillUsapPool(new int[]{ sessionSocket.getFileDescriptor().getInt$() });
} else {
Zygote.emptyUsapPool();
return null;
}
}
这里newStatus
就是mUsapPoolEnabled
,我们这里为true
开启UsapPool
,如果这个值为false
,就是关闭UsapPool
。我们直接看fillUsapPool
:
Runnable fillUsapPool(int[] sessionSocketRawFDs) {
....
if (usapPoolCount < mUsapPoolSizeMin
|| numUsapsToSpawn >= mUsapPoolRefillThreshold) {
....
while (usapPoolCount++ < mUsapPoolSizeMax) {
Runnable caller = Zygote.forkUsap(mUsapPoolSocket, sessionSocketRawFDs);
if (caller != null) {
return caller;
}
}
....
}
....
return null;
}
这里判断如果当前USAP进程小于最大USAP进程,则调用Zygote.forkUsap
,这里注意传入的mUsapPoolSocket
参数,他在ZygoteServer
构造函数中初始化了:
mUsapPoolSocket = Zygote.createManagedSocketFromInitSocket(Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);
public static final String USAP_POOL_PRIMARY_SOCKET_NAME = "usap_pool_primary";
我们的AMS之后会通过这个USAP_POOL_PRIMARY_SOCKET_NAME
创建LocalSocket与USAP进程通信的。
不过你可能意识到了,这些fork出的进程将会监听在同一个SocketServer上,这里就是一个技术细节了:
如果多个进程或者线程在等待同一个事件,当事件发生时,所有线程和进程都会被内核唤醒,唤醒后通常只有一个进程获得了该事件并进行处理,其他进程发现获取事件失败后又继续进入了等待状态,在一定程度上降低了系统性能,这称为 惊群效应。
这里多个 USAP 共同监听了同一个 Socket,而在 Linux Kernel 2.6 后 Socket 的 accept() 通过维护一个等待队列来解决这一问题,因此这段代码中避免了惊群效应
回到fillUsapPool
的Zygote.forkUsap
函数:
static Runnable forkUsap(LocalServerSocket usapPoolSocket,
int[] sessionSocketRawFDs) {
FileDescriptor[] pipeFDs = null;
try {
pipeFDs = Os.pipe2(O_CLOEXEC);
} catch (ErrnoException errnoEx) {
throw new IllegalStateException("Unable to create USAP pipe.", errnoEx);
}
int pid =
nativeForkUsap(pipeFDs[0].getInt$(), pipeFDs[1].getInt$(), sessionSocketRawFDs);
if (pid == 0) {
IoUtils.closeQuietly(pipeFDs[0]);
return usapMain(usapPoolSocket, pipeFDs[1]);
} else {
// The read-end of the pipe will be closed by the native code.
// See removeUsapTableEntry();
IoUtils.closeQuietly(pipeFDs[1]);
return null;
}
}
这里通过底层函数fork出了新的进程。在子进程中调用了usapMain
:
private static Runnable usapMain(LocalServerSocket usapPoolSocket,
FileDescriptor writePipe) {
....
while (true) {
try {
//等待socket客户端的连接
sessionSocket = usapPoolSocket.accept();
....
} catch (Exception ex) {
Log.e("USAP", ex.getMessage());
IoUtils.closeQuietly(sessionSocket);
// Re-enable SIGTERM so the USAP can be flushed from the pool if necessary.
unblockSigTerm();
}
}
try {
....
specializeAppProcess(args.mUid, args.mGid, args.mGids,
args.mRuntimeFlags, rlimits, args.mMountExternal,
args.mSeInfo, args.mNiceName, args.mStartChildZygote,
args.mInstructionSet, args.mAppDataDir);
if (args.mNiceName != null) {
Process.setArgV0(args.mNiceName);
}
// End of the postFork event.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return ZygoteInit.zygoteInit(args.mTargetSdkVersion,
args.mRemainingArgs,
null /* classLoader */);
} finally {
// Unblock SIGTERM to restore the process to default behavior.
unblockSigTerm();
}
}
这里调用usapPoolSocket.accept()
阻塞等待客户端连接,这个客户端就是是来自AMS的请求,后面有提到。接收到请求之后specializeAppProcess
将进程''specialize''成app进程,然后调用我们熟悉的ZygoteInit.zygoteInit
最终执行到了ActivityThread
的main
函数。
我们再来看AMS是如何和USAP进程通信的,首先回到本文开始的ZygoteProcess
的startViaZygote
方法:
private Process.ProcessStartResult startViaZygote(....) throws ZygoteStartFailedEx {
....
synchronized(mLock) {
// The USAP pool can not be used if the application will not use the systems graphics
// driver. If that driver is requested use the Zygote application start path.
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
useUsapPool,
argsForZygote);
}
}
来看zygoteSendArgsAndGetResult
:
private Process.ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, boolean useUsapPool, @NonNull ArrayList<String> args)
throws ZygoteStartFailedEx {
....
if (useUsapPool && mUsapPoolEnabled && canAttemptUsap(args)) {
try {
return attemptUsapSendArgsAndGetResult(zygoteState, msgStr);
} catch (IOException ex) {
// If there was an IOException using the USAP pool we will log the error and
// attempt to start the process through the Zygote.
Log.e(LOG_TAG, "IO Exception while communicating with USAP pool - "
+ ex.getMessage());
}
}
return attemptZygoteSendArgsAndGetResult(zygoteState, msgStr);
}
如果使用USAP就调用attemptUsapSendArgsAndGetResult(zygoteState, msgStr)
,我们显然是使用的情况:
private Process.ProcessStartResult attemptUsapSendArgsAndGetResult(
ZygoteState zygoteState, String msgStr)
throws ZygoteStartFailedEx, IOException {
try (LocalSocket usapSessionSocket = zygoteState.getUsapSessionSocket()) {
final BufferedWriter usapWriter =
new BufferedWriter(
new OutputStreamWriter(usapSessionSocket.getOutputStream()),
Zygote.SOCKET_BUFFER_SIZE);
final DataInputStream usapReader =
new DataInputStream(usapSessionSocket.getInputStream());
usapWriter.write(msgStr);
usapWriter.flush();
Process.ProcessStartResult result = new Process.ProcessStartResult();
result.pid = usapReader.readInt();
// USAPs can't be used to spawn processes that need wrappers.
result.usingWrapper = false;
if (result.pid >= 0) {
return result;
} else {
throw new ZygoteStartFailedEx("USAP specialization failed");
}
}
}
getUsapSessionSocket
通过 mUsapSocketAddress
创建LocalSocket():
LocalSocket getUsapSessionSocket() throws IOException {
final LocalSocket usapSessionSocket = new LocalSocket();
usapSessionSocket.connect(this.mUsapSocketAddress);
return usapSessionSocket;
}
mUsapSocketAddress
的值如下:
mUsapPoolSocketAddress =new LocalSocketAddress(Zygote.USAP_POOL_PRIMARY_SOCKET_NAME,LocalSocketAddress.Namespace.RESERVED);
public static final String USAP_POOL_PRIMARY_SOCKET_NAME = "usap_pool_primary";
和上面说到的USAP阻塞等待的socket
地址是一致的,这里就和上面的内容接上了。回到attemptUsapSendArgsAndGetResult
,socket连接成功后,获取输入输出流,并写入参数,读取USAP进程的pid,和直接和Zygote进程通信是一致的。