前言
本篇是 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 发动进程(下)