前言
本篇是 Activity 发动进程(中)篇,接受上篇文章 Android Activity 发动进程(上)
本文中代码依据 android-security-11.0.0_r66 分支.
Android Activity 发动进程(上) – 主要描绘SystemServer 进程怎样处理startActivity恳求以及怎样走到恳求Zygote fork app 进程的流程
Android Activity 发动进程(下) – 主要描绘 App进程发动后绑定到SystemServer可被体系监控进程 和 根 Activity 创立进程
此篇主要是介绍Zygote进程接收到 来自SystemServer进程恳求创立App进程的音讯后,怎样去fork app 进程。
3. Zygote 进程 fork App进程进程
frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
ArrayList<ZygoteConnection> peers = new ArrayList<>();
// 省掉
mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
while (true) {
fetchUsapPoolPolicyPropsWithMinInterval();
mUsapPoolRefillAction = UsapPoolRefillAction.NONE;
// 省掉
try {
pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);//chld 正常情况下此处传入的值是 -1,即一向轮询等待数据
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
if (pollReturnValue == 0) {
mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
} else {
boolean usapPoolFDRead = false;
while (--pollIndex >= 0) {
if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
continue;
}
if (pollIndex == 0) {//chld - 走到 pollIndex 说明此刻没有需求处理的创立进程的需求,而是有的新的衔接到来
// Zygote server socket
ZygoteConnection newPeer = acceptCommandPeer(abiList);// chld 等待 新的链接,没有时 阻塞着
peers.add(newPeer);
socketFDs.add(newPeer.getFileDescriptor());
} else if (pollIndex < usapPoolEventFDIndex) { // chld 说明此刻体系不支持 usap.(如果有的话,pollIndex 值是大于等于 usapPoolEventFDIndex 的,原因具体情况Android 11 对应代码,此处相关逻辑已被删除)
// Session socket accepted from the Zygote server socket
try {
ZygoteConnection connection = peers.get(pollIndex);
final Runnable command = connection.processOneCommand(this);// 代码1
// TODO (chriswailes): Is this extra check necessary?
if (mIsForkChild) {// chld 此刻处于fork出来的运用进程中,回来 Runnable 目标,去在`ZygoteInit::main`办法中履行
// We're in the child. We should always have a command to run at
// this stage if processOneCommand hasn't called "exec".
if (command == null) {
throw new IllegalStateException("command == null");
}
return command;
} else {
if (connection.isClosedByPeer()) {//代码2 Zygote 进程关闭 socket 衔接
connection.closeSocket();
peers.remove(pollIndex);
socketFDs.remove(pollIndex);
}
}
} catch (Exception e) {
// 省掉
} finally {
// 重置标志
mIsForkChild = false;
}
} else {// chld USAP 发动
// 省掉
usapPoolFDRead = true;
}
}
if (usapPoolFDRead) {
int usapPoolCount = Zygote.getUsapPoolCount();
if (usapPoolCount < mUsapPoolSizeMin) {//chld 最小值默许是1,即表明此刻 可用USAP进程小于1个
// Immediate refill
mUsapPoolRefillAction = UsapPoolRefillAction.IMMEDIATE;
} else if (mUsapPoolSizeMax - usapPoolCount >= mUsapPoolRefillThreshold) {//chld 可运用的USAP 进程低于阈值(最大10个,阈值一般是最大值的一半)
// Delayed refill
mUsapPoolRefillTriggerTimestamp = System.currentTimeMillis();
}
}
}
if (mUsapPoolRefillAction != UsapPoolRefillAction.NONE) {// chld 说明此刻运用 usap 办法发动
int[] sessionSocketRawFDs =
socketFDs.subList(1, socketFDs.size())
.stream()
.mapToInt(FileDescriptor::getInt$)
.toArray();
final boolean isPriorityRefill =
mUsapPoolRefillAction == UsapPoolRefillAction.IMMEDIATE;// chld 判别是否立行将usap 进程弥补完
final Runnable command =
fillUsapPool(sessionSocketRawFDs, isPriorityRefill);// 代码3
if (command != null) {
return command;
} else if (isPriorityRefill) {
// Schedule a delayed refill to finish refilling the pool.
mUsapPoolRefillTriggerTimestamp = System.currentTimeMillis();
}
}
}
}
代码3 处,是在运用usap 办法发动。此处暂时不细讲了,有爱好的同学自行查找下吧,usap 仅仅免除了进程的fork,加快了运用发动速度,其他流程根本共同。(USAP 进程默许会创立10个待用,待低于一半值时重新填空完成,同时其是在防止影响运用进程创立的,在没有运用创立恳求的时分去创立) 而在未开启 usap 发动办法的情况下,正常是走到代码1处, processOneCommand 回来一个 Runnable 目标,该 Runnable 目标将在 ZygnoteInit::main 被履行(注:在Android 8.1 及之前,是经过抛出反常的办法抛到 ZygnoteInit::main 办法中被捕获后履行的,一种奇奇怪怪的古怪办法,在Android 9开始终于才用 回来的办法了)。 该 Runnable目标的 run办法中将履行 ActivityThread 的创立办法。
接下来看下 ZygoteConnection::processOneCommand 办法吧
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
Runnable processOneCommand(ZygoteServer zygoteServer) {
String[] args;
try {
args = Zygote.readArgumentList(mSocketReader);
} catch (IOException ex) {
throw new IllegalStateException("IOException on command socket", ex);
}
if (args == null) {
isEof = true;
return null;
}
ZygoteArguments parsedArgs = new ZygoteArguments(args);//代码1
//... 省掉
// 代码2
pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList,
parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);
try {
if (pid == 0) {//代码3 子进程
zygoteServer.setForkChild();
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);// 关闭与server的通道
serverPipeFd = null;
return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);// 代码4
} else {
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
handleParentProc(pid, serverPipeFd);// 代码5
// 代码6
return null;
}
} finally {
// 最终再尝试一次关闭管道
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
代码1处 将 SystemServer发送来的恳求数参数进行转换成 ZygoteArguments 目标。 代码2处 运用将依据这些参数调用 Zygote.forkAndSpecialize办法去fork 一个进程,forkAndSpecialize 下面的处理就涉及作者盲区了。 代码3处 fork后进入代码3判别处,此处依据 pid 的值判别当时是处于fork 处理的子进程仍是 父进程。 子线程将进入代码4处调用 handleChildProc 办法处理。而 Zygote 将进入代码5处,并在代码6处回来 null,经过null 来告知上层,此刻是处于子进程中。并且在 handleParentProc办法中 Zygote 进程将告知 SystemServer 创立的进程的id.
先看下 handleParentProc办法。
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
private void handleParentProc(int pid, FileDescriptor pipeFd) {
//... 省掉
try {
mSocketOutStream.writeInt(pid);
mSocketOutStream.writeBoolean(usingWrapper);
} catch (IOException ex) {
throw new IllegalStateException("Error writing to command socket", ex);
}
}
此处经过socket 将进程pid 和 usingWrapper 数据传回给了 SysmtemServer 进程的 2.6 章节处 的 ZygoteProcess::attemptZygoteSendArgsAndGetResult办法中.
接下来回来看下 ZygoteConnection::handleChildProc 办法中是怎样去创立一个 Runnable 目标的。
private Runnable handleChildProc(ZygoteArguments parsedArgs,
FileDescriptor pipeFd, boolean isZygote) {
closeSocket();
Zygote.setAppProcessName(parsedArgs, TAG);//此刻设置进程名称
// chld 运用进程此处 mInvokeWith:null isZygote:false
// 输出结果: I/Zygote: chld handleChildProc setAppProcessName mNiceName:com.example.myapplication2 mPackageName:com.example.myapplication2 mInvokeWith:null isZygote:false
Log.i(TAG,"chld handleChildProc setAppProcessName mNiceName:" + parsedArgs.mNiceName + " mPackageName:" + parsedArgs.mPackageName + " mInvokeWith:" + parsedArgs.mInvokeWith +" isZygote:" + isZygote);
if (parsedArgs.mInvokeWith != null) {
WrapperInit.execApplication(parsedArgs.mInvokeWith,
parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.mRemainingArgs);
// Should not get here.
throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
} else {
if (!isZygote) {
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, null /* classLoader */);
} else {// chld isZygote 判别创立的这个新的进程自身是不是一个Zygote(孵化器) 进程,创立的是运用进程显然是走此处
return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mRemainingArgs, null /* classLoader */);//代码2
}
}
}
代码1 处此刻设置了进程的姓名,一般是运用的包名。在此之前 Zygote名称为 Zygote,有爱好的同学能够试试。注意:如果是UASP 办法发动,在调用 Zygote.setAppProcessName之前名称为 UASP 。 代码2 这个Runnable 目标还得再经过 ZygoteInit::childZygoteInit 办法去创立啊。此处还需求的参数只剩下了 parsedArgs.mTargetSdkVersion,parsedArgs.mRemainingArgs
接下来去看下 ZygoteInit::childZygoteInit 办法的实现吧。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
static final Runnable childZygoteInit(
int targetSdkVersion, String[] argv, ClassLoader classLoader) {
RuntimeInit.Arguments args = new RuntimeInit.Arguments(argv);
return RuntimeInit.findStaticMain(args.startClass, args.startArgs, classLoader);
}
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
protected static Runnable findStaticMain(String className, String[] argv,
ClassLoader classLoader) {
Class<?> cl;
try {
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}
/*
* This throw gets caught in ZygoteInit.main(), which responds
* by invoking the exception's run() method. This arrangement
* clears up all the stack frames that were required in setting
* up the process.
*/
return new MethodAndArgsCaller(m, argv);// 代码1
}
findStaticMain 办法中传入的 className 就是 Android Activity 发动进程(上)2.5 章节 ProcessList ProcessList::startProcessLocked 办法中 final String entryPoint = "android.app.ActivityThread" 值;, 此处获取了该类的静态办法,并将参数传入。 然后一路回来到 ZygoteInit::main办法吧。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
ZygoteServer zygoteServer = null;
Runnable caller;
try {
//...
zygoteServer = new ZygoteServer(isPrimaryZygote);
if (startSystemServer) {// 代码1 fork SystemServer 进程
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
// {@code r == null} in the parent (zygote) process, and {@code r != null} in the
// child (system_server) process.
if (r != null) {
r.run();
return;
}
}
Log.i(TAG, "Accepting command socket connections");
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = zygoteServer.runSelectLoop(abiList);// 代码2 等待AMS创立进行恳求,当创立完运用子进程后,运用子进程会回来一个`Runnable` 目标
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
} finally {
if (zygoteServer != null) {
zygoteServer.closeServerSocket();
}
}
if (caller != null) {//chld 运用进程f
caller.run();
}
}
能够看到此处履行了 MethodAndArgsCaller::run办法,在该 run办法中去进行了 ActivityAThread::main 办法的反射调用。
为什么不直接在
RuntimeInit::findStaticMain调用呢? 这是为了清除 创立进程中堆栈,让运用进程看起来是直接从ZygoteInit::main办法中直接发动的。(太长的栈,那遇到反常打印出来,得带着多少无用的栈数据啊)
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
static class MethodAndArgsCaller implements Runnable {
/** method to call */
private final Method mMethod;
/** argument array */
private final String[] mArgs;
public MethodAndArgsCaller(Method method, String[] args) {
mMethod = method;
mArgs = args;
}
public void run() {
try {
mMethod.invoke(null, new Object[] { mArgs });
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(ex);
}
}
}
如上是 MethodAndArgsCaller类,run 没什么可说的,就是一个反射调用。接下来就走到 ActivityThread类中啦,也真正的进入运用层了。
接下来请看下篇
Android Activity 发动进程(下)
