Zygote进程中的Socket服务
前面说了Zygote进程是Android中Java世界的第一个进程,后续所有的Java进程都从Zygote进程fork出来。Zygote进程启动起来之后,主要做了两个事情:
- fork出System Server进程
- 进入循环等待状态,等待fork进程的请求
System Server进程的启动在其他文章中会讲述,本文就来看看Zygote是如何搭建socket服务的。这部分代码在ZygoteInit.java main方法中:
ZygoteInit.java main方法
public static void main(String argv[]) {
ZygoteServer zygoteServer = null;
//...
final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME); //true
zygoteServer = new ZygoteServer(isPrimaryZygote);//创建一个ZygoteServer对象,isPrimaryZygote 用来区分是zygote还是zygote的子进程
//...
//zygote进程会走到这里
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.
//zygote进程开启循环 ,等待socket连接,有请求的时候,就去fork新进程
caller = zygoteServer.runSelectLoop(abiList);//返回来的caller是子进程的Runnable,
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
} finally {
if (zygoteServer != null) {//断开socket连接
zygoteServer.closeServerSocket();
}
}
// We're in the child process and have exited the select loop. Proceed to execute the
// command.
if (caller != null) {//非System server子进程,在这里调用,子进程开始自己的任务
caller.run();
}
}
总结
代码也比较简单
- 创建ZygoteServer对象
- 调用ZygoteServer的 runSelectLoop 方法,获取子进程的执行任务。
ZygoteServer.java runSelectLoop 方法
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) {
//... 一大堆代码 建立socket相关策略的 不用管,管不了那么细
int pollReturnValue;
try {
pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
if (pollReturnValue == 0) {
// The poll timeout has been exceeded. This only occurs when we have finished the
// USAP pool refill delay period. 超出了轮询超时。 仅当我们完成USAP池重新填充延迟时间后,才会发生这种情况。
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);
final Runnable command = connection.processOneCommand(this);
// 调用了 connection.processOneCommand方法就出现两个进程了,多以下面的代码需要对进程进行判断
// 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 processOneCommand 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 processOneCommand. 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) {
if (!mIsForkChild) {
//在zygote进程出现异常,确保套接字连接关闭
ZygoteConnection conn = peers.remove(pollIndex);
conn.closeSocket();
socketFDs.remove(pollIndex);
} else {
//新创建的进程异常,直接抛出去
Log.e(TAG, "Caught post-fork exception in child process.", e);
throw e;
}
} finally {
//重置状态
mIsForkChild = false;
}
} else {
//... socket策略出问题走这里吧
}
}
//socket池检查
}
//...这部分也没看懂
}
总结
这个方法好难,好多代码没看懂,不过主要的还是看明白了一点
- 进入死循环,调用acceptCommandPeer等待socket客户端连接
- 掉用ZygoteConnection类的processOneCommand方法获取子进程的执行任务
- mIsForkChild的值在ZygoteConnection中设置的
ZygoteConnection.java processOneCommand方法
Runnable processOneCommand(ZygoteServer zygoteServer) {
String[] args;
try {
args = Zygote.readArgumentList(mSocketReader);//读取socket连接参数
} catch (IOException ex) {
throw new IllegalStateException("IOException on command socket", ex);
}
if (args == null) { //参数为空直接返回
isEof = true;
return null;
}
int pid;
FileDescriptor childPipeFd = null;
FileDescriptor serverPipeFd = null;
ZygoteArguments parsedArgs = new ZygoteArguments(args);//封装参数
//... 开启一大堆参数 条件判断,直接不看了
int [] fdsToClose = { -1, -1 };
FileDescriptor fd = mSocket.getFileDescriptor();
if (fd != null) {
fdsToClose[0] = fd.getInt$();
}
fd = zygoteServer.getZygoteSocketFileDescriptor();
if (fd != null) {
fdsToClose[1] = fd.getInt$();
}
//开始fork新进程
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);
try {
if (pid == 0) { // 新进程的pid 值是 0
// in child
zygoteServer.setForkChild();//这个就是设置前面的mIsForkChild值
zygoteServer.closeServerSocket(); //关闭socket
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
// 找到新进程的执行任务
return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
} else {
// In the parent. A pid < 0 indicates a failure and will be handled in
// handleParentProc. zygote进程
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
handleParentProc(pid, serverPipeFd);
return null;// zygote进程没有执行任务了,返回null
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
总结
这个方法还好不是很难
- 读取socket连接的参数,没有参数直接退出了
- 根据参数做些工作(具体什么工作,没细看)
- 调用Zygote.forkAndSpecialize方法fork新进程
- 开始有两个进程了,所以这里zygote进程和新进程开始分道扬镳了
zygote进程:调用handleParentProc ,返回null
新进程:调用handleChildProc方法找到当前接下来进行的执行任务,并返回(还记得在ZygoteInit.java的main方法会执行这个任务吧)
Zygote.java forkAndSpecialize方法
public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
boolean isTopApp) {
ZygoteHooks.preFork();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp);
if (pid == 0) {
// Note that this event ends at the end of handleChildProc, 请注意,此事件在handleChildProc的结尾处结束,
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
}
// Set the Java Language thread priority to the default value for new apps.
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
ZygoteHooks.postForkCommon();
return pid;
}
private static native int nativeForkAndSpecialize(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
String appDataDir, boolean isTopApp);
总结
这个方法简单了,就是调用native层代码fork新进程,返回pid
ZygoteConnection.java handleParentProc方法
/**
* Handles post-fork cleanup of parent proc
* 下面这一句的代码提示,如果是新进程的pid,那么pid=0,不然是错误的fork
* @param pid != 0; pid of child if > 0 or indication of failed fork
* if < 0;
* @param pipeFd null-ok; pipe for communication with child.
*/
private void handleParentProc(int pid, FileDescriptor pipeFd) {
if (pid > 0) { //不是自己的子进程,设置pid到对应的进程组,啥进程组?不知道呀
setChildPgid(pid);
}
boolean usingWrapper = false;
if (pipeFd != null && pid > 0) {//有方法前的注释知道,子进程的pid是0,所以不走这里面
//...
}
try {
mSocketOutStream.writeInt(pid); //向socket客户段 写 pid
mSocketOutStream.writeBoolean(usingWrapper);//向socket客户段 写 false
} catch (IOException ex) {
throw new IllegalStateException("Error writing to command socket", ex);
}
}
总结
这个方法一大串代码,不过似乎在创建新进程的时候,pid=0所以很多都不执行???
- 向socket客户端输出pid及一个布尔值false
private Runnable handleChildProc(ZygoteArguments parsedArgs,
FileDescriptor pipeFd, boolean isZygote) {
/*
* By the time we get here, the native code has closed the two actual Zygote
* socket connections, and substituted /dev/null in their place. The LocalSocket
* objects still need to be closed properly.
* 到我们到达这里时,本机代码已关闭两个实际的Zygote套接字连接,并在其位置替换了/ dev / null。 LocalSocket对象仍然需要正确关闭。
*/
closeSocket();//关闭本地是socket?
Zygote.setAppProcessName(parsedArgs, TAG);//设置进程名字
// End of the postFork event.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
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) {//这个参数是从客户端传过来的,看命名应该判断是不是zygote,猜测子进程应该走else吧
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, null /* classLoader */);
} else {
return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mRemainingArgs, null /* classLoader */);
}
}
}
总结
这个方法主要是找到新进程后续的执行任务
确保本地连接关闭
设置进程名字
根据客户端参数,调用相应的初始化方法
ZygoteInit.java zygoteInit方法
public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
if (RuntimeInit.DEBUG) {
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
RuntimeInit.redirectLogStreams();
RuntimeInit.commonInit();
ZygoteInit.nativeZygoteInit();
return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
classLoader);
}
总结
这个方法应该是Zygote初始化的方法
RuntimeInit.java applicationInit方法
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
// If the application calls System.exit(), terminate the process
// immediately without running any shutdown hooks. It is not possible to
// shutdown an Android application gracefully. Among other things, the
// Android runtime shutdown hooks close the Binder driver, which can cause
// leftover running threads to crash before the process actually exits.
nativeSetExitWithoutCleanup(true);
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
final Arguments args = new Arguments(argv);
// The end of of the RuntimeInit event (see #zygoteInit).
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// Remaining arguments are passed to the start class's static main
return findStaticMain(args.startClass, args.startArgs, classLoader);
}
这个方法简单。。。。
zygoteInit.java childZygoteInit方法
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);
}
总结
这个方法简单,一目了然。
ZygoteInit.java zygoteInit方法和zygoteInit.java childZygoteInit方法最终都走RuntimeInit.findStaticMain方法
RuntimeInit.java findStaticMain方法
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);
}
这个方法简单。通过反射找到类的main方法,创建MethodAndArgsCaller类的对象返回。MethodAndArgsCaller类继承Runnable接口,到这里新进程的后续执行任务就找到了
RuntimeInit.java MethodAndArgsCaller 类
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);
}
}
}
这个类实现Runnable接口,为新进程创建执行任务提供能力。根据传进来的方法,在run方法中执行。前面说到在ZegoteInit.java的main方法中,新进程最终会返回一个Runnable,随后调用run方法。就是这里的run方法。
最终终结
Zygote的socket服务时刻在等待客户端的连接,客户端携带参数过来,Zygote负责fork出新进程。然后新进程根据socket客户端的参数创建对应的执行任务,最终执行任务。这个任务是:执行socket客户端携带的一个类名对应的类的main方法。