Zygote进程中的Socket服务

​ 前面说了Zygote进程是Android中Java世界的第一个进程,后续所有的Java进程都从Zygote进程fork出来。Zygote进程启动起来之后,主要做了两个事情:

  1. fork出System Server进程
  2. 进入循环等待状态,等待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();
    }
}

总结

代码也比较简单

  1. 创建ZygoteServer对象
  2. 调用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池检查
            }

            //...这部分也没看懂
    }

总结

这个方法好难,好多代码没看懂,不过主要的还是看明白了一点

  1. 进入死循环,调用acceptCommandPeer等待socket客户端连接
  2. 掉用ZygoteConnection类的processOneCommand方法获取子进程的执行任务
  3. 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);
        }
    }

总结

这个方法还好不是很难

  1. 读取socket连接的参数,没有参数直接退出了
  2. 根据参数做些工作(具体什么工作,没细看)
  3. 调用Zygote.forkAndSpecialize方法fork新进程
  4. 开始有两个进程了,所以这里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 &gt; 0 or indication of failed fork
     * if &lt; 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所以很多都不执行???

  1. 向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 */);
            }
        }
    }

总结

这个方法主要是找到新进程后续的执行任务

  1. 确保本地连接关闭

  2. 设置进程名字

  3. 根据客户端参数,调用相应的初始化方法

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方法。

results matching ""

    No results matching ""