從Android Q(10)開始剃法,Google引入了一種新的機(jī)制俄占,加快了app的啟動(dòng)時(shí)間舔痕,具體請(qǐng)看Android Framework | 一種新型的應(yīng)用啟動(dòng)機(jī)制:USAP食听,本篇將會(huì)詳細(xì)介紹USAP 進(jìn)程啟動(dòng)的流程奸披。
從Activity啟動(dòng)流程 上篇(Android 10),我們得知在Activity啟動(dòng)過程中蝎土,我們會(huì)調(diào)用到\frameworks\base\core\java\android\os\ZygoteProcess.java
的start
方法视哑,然后調(diào)用startViaZygote()
,其實(shí)在調(diào)用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);
}
}
從函數(shù)名稱誊涯,我們可以得知挡毅,這里用fetchUsapPoolEnabledPropWithMinInterval
判斷系統(tǒng)是否開啟了USAP功能,如果開啟則調(diào)用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);
}
}
}
可以看到該函數(shù)將調(diào)用attemptConnectionToPrimaryZygote
或attemptConnectionToSecondaryZygote()
這其實(shí)和Zygote()運(yùn)行的位數(shù)有關(guān)暴构,32或者64位慷嗜,他們都調(diào)用了ZygoteState.connect
,只是傳入的參數(shù)不同丹壕,我們看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));
}
這段代碼比較好理解庆械,就是創(chuàng)建一個(gè)LocalSocket
和Zygote
建立連接,并獲取輸入輸出流設(shè)置到ZygoteState
中菌赖,待會(huì)兒我們會(huì)用到缭乘,至此attemptConnectionToPrimaryZygote
調(diào)用完成,回到上面的informZygotesOfUsapPoolStatus
琉用,代碼將command
寫給了Zygote堕绩,而command
為"1\n--usap-pool-enabled=" + mUsapPoolEnabled + "\n"
,這里mUsapPoolEnabled
自然為true
邑时, 記住這個(gè)command
奴紧,接下來可以到zygote
進(jìn)程中查看了。
在Android系統(tǒng)啟動(dòng)流程末尾晶丘,我們說到Zygote進(jìn)程會(huì)執(zhí)行zygoteServer.runSelectLoop(abiList)
黍氮,接收并處理AMS傳過來的消息唐含,比如fork app進(jìn)程。這里我們直接看runSelectLoop
函數(shù)即可:
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
會(huì)使用epoll
機(jī)制沫浆,阻塞在Os.poll(pollFDs, -1)
捷枯,獲取對(duì)方連接請(qǐng)求后,執(zhí)行\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;
}
....
還記的我們傳入的參數(shù)是什么嗎专执?"1\n--usap-pool-enabled=" + mUsapPoolEnabled + "\n"
淮捆,這里就將mUsapPoolStatusSpecified
設(shè)置為true
,mUsapPoolEnabled
設(shè)置為 mUsapPoolEnabled
也為true
本股。所以會(huì)執(zhí)行
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
,如果這個(gè)值為false
拄显,就是關(guān)閉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;
}
這里判斷如果當(dāng)前USAP進(jìn)程小于最大USAP進(jìn)程,則調(diào)用Zygote.forkUsap
凿叠,這里注意傳入的mUsapPoolSocket
參數(shù),他在ZygoteServer
構(gòu)造函數(shù)中初始化了:
mUsapPoolSocket = Zygote.createManagedSocketFromInitSocket(Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);
public static final String USAP_POOL_PRIMARY_SOCKET_NAME = "usap_pool_primary";
我們的AMS之后會(huì)通過這個(gè)USAP_POOL_PRIMARY_SOCKET_NAME
創(chuàng)建LocalSocket與USAP進(jìn)程通信的嚼吞。
不過你可能意識(shí)到了盒件,這些fork出的進(jìn)程將會(huì)監(jiān)聽在同一個(gè)SocketServer上,這里就是一個(gè)技術(shù)細(xì)節(jié)了:
如果多個(gè)進(jìn)程或者線程在等待同一個(gè)事件舱禽,當(dāng)事件發(fā)生時(shí)炒刁,所有線程和進(jìn)程都會(huì)被內(nèi)核喚醒,喚醒后通常只有一個(gè)進(jìn)程獲得了該事件并進(jìn)行處理誊稚,其他進(jìn)程發(fā)現(xiàn)獲取事件失敗后又繼續(xù)進(jìn)入了等待狀態(tài)翔始,在一定程度上降低了系統(tǒng)性能,這稱為 驚群效應(yīng)里伯。
這里多個(gè) USAP 共同監(jiān)聽了同一個(gè) Socket城瞎,而在 Linux Kernel 2.6 后 Socket 的 accept() 通過維護(hù)一個(gè)等待隊(duì)列來解決這一問題,因此這段代碼中避免了驚群效應(yīng)
回到fillUsapPool
的Zygote.forkUsap
函數(shù):
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;
}
}
這里通過底層函數(shù)fork出了新的進(jìn)程疾瓮。在子進(jìn)程中調(diào)用了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();
}
}
這里調(diào)用usapPoolSocket.accept()
阻塞等待客戶端連接脖镀,這個(gè)客戶端就是是來自AMS的請(qǐng)求,后面有提到狼电。接收到請(qǐng)求之后specializeAppProcess
將進(jìn)程''specialize''成app進(jìn)程蜒灰,然后調(diào)用我們熟悉的ZygoteInit.zygoteInit
最終執(zhí)行到了ActivityThread
的main
函數(shù)。
我們?cè)賮砜碅MS是如何和USAP進(jìn)程通信的肩碟,首先回到本文開始的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就調(diào)用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
創(chuàng)建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
地址是一致的,這里就和上面的內(nèi)容接上了削祈〕崮纾回到attemptUsapSendArgsAndGetResult
,socket連接成功后,獲取輸入輸出流未巫,并寫入?yún)?shù)窿撬,讀取USAP進(jìn)程的pid,和直接和Zygote進(jìn)程通信是一致的叙凡。