Zygote的工作原理

2,925 阅读4分钟

android源码分析目录

(注:以下代码分析基于android-10.0.0_r30)

一 概述

之前介绍了关于Zygote的启动过程,这一篇再来详细说说Zygote的工作原理

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
frameworks/base/core/java/android/net/LocalServerSocket.java
frameworks/base/core/java/android/net/LocalSocket.java
frameworks/base/core/java/com/android/internal/os/Zygote.java
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

二 ZygoteInit

Zygote工作的入口,是在ZygoteInit中的几行代码开启的

[ZygoteInit.java]

Runnable caller;
//创建一个ZygoteServer,传入的是一个boolean变量
zygoteServer = new ZygoteServer(isPrimaryZygote);
//通过zygoteServer获取一个run对象
caller = zygoteServer.runSelectLoop(abiList);
...
if (caller != null) {
    //开始loop循环
    caller.run();
}

三 ZygoteServer.java

3.1 ZygoteServer的构造函数

[ZygoteServer.java]

ZygoteServer(boolean isPrimaryZygote) {
    //获取一个UsapPoolEventFD,这个fd是用ZygoteServer轮询的
    mUsapPoolEventFD = Zygote.getUsapPoolEventFD();

    if (isPrimaryZygote) {
        //创建一个mZygoteSocket,这个ZygoteSocket其实是LocalServerSocket
        //在LocalServerSocket里面有一个实现类LocalSocketImpl,在这个创建方法里面会创建一个fd
        //之后的轮询里面会用到这个fd
        mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
        //创建一个UsapPoolSocket
        mUsapPoolSocket =
                Zygote.createManagedSocketFromInitSocket(
                        Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);
    } else {
        mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME);
        mUsapPoolSocket =
                Zygote.createManagedSocketFromInitSocket(
                        Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);
    }

    fetchUsapPoolPolicyProps();

    mUsapPoolSupported = true;
}

在ZygoteServer的构造函数里面,会创建一个ZygoteSocket,它其实是一个LocalServerSocket,这个类里面有一个对应的实现类LocalSocketImpl和一个fd,因为在Android中,Zygote和AMS的通信使用的是sock而不是binder,而socket又是C/S架构的,所以此时作为Server端的ZygoteServer,持有的是一个LocalServerSocket,如果Client端想要通信,那么就需要使用对应的Client端的LocalSocket

3.2 runSelectLoop

runSelectLoop这个方法较长,虽然用于处理Client的socket消息,但是因为引入了UsapPool,所以有很多UsapPool的相关逻辑,不过在runSelectLoop中,最主要的处理逻辑,都是通过ZygoteConnection来完成的

[ZygoteServer.java]

Runnable runSelectLoop(String abiList) {
    //创建两个数组
    //数组socketFDs里面存放的是一些fd,用来做socket通信的
    //数组peers里面存放的是ZygoteConnection
    ArrayList<FileDescriptor> socketFDs = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

    //第0位添加一个从ZygoteSocket获取的fd
    socketFDs.add(mZygoteSocket.getFileDescriptor());
    peers.add(null);

    while (true) {
        //这前面的一大段逻辑都是处理UsapPool相关的
        //并且会将socketFDs进行一些处理,构造出一个structs poll的结构体,然后通过USAP连接池进行处理
        //根据Zygote的USAP池的状态有不同的Zygote(可以是常规Zygote,WebView Zygote或AppZygote)。
        ...

        try {
            //等待事件socket的fd事件
            Os.poll(pollFDs, -1);
        } catch (ErrnoException ex) {
            throw new RuntimeException("poll failed", ex);
        }

        boolean usapPoolFDRead = false;

        while (--pollIndex >= 0) {
            if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
                continue;
            }

            if (pollIndex == 0) {
                // ZygoteSocket收到请求,通过acceptCommandPeer创建一个ZygoteConnection
                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);
                    //处理socket消息
                    final Runnable command = connection.processOneCommand(this);
                    //在Child端
                    if (mIsForkChild) {

                        if (command == null) {
                            throw new IllegalStateException("command == null");
                        }

                        return command;
                    } else {
                        //在Server端
                        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) {
                        //在server端,因此这里发生的任何异常之前都是
                        //在处理命令或从读写socket。 对此做一个提醒以便
                        //确切知道失败的原因。
                        // We're in the server so any exception here is one that has taken place
                        // pre-fork while processing commands or reading / writing from the
                        // control socket. Make a loud noise about any such exceptions so that
                        // we know exactly what failed and why.

                        Slog.e(TAG, "Exception executing zygote command: ", e);

                        //关闭socket避免超时
                        ZygoteConnection conn = peers.remove(pollIndex);
                        conn.closeSocket();

                        socketFDs.remove(pollIndex);
                    } else {
                        //在child端,异常发生在ActivityThread.main之前

                        Log.e(TAG, "Caught post-fork exception in child process.", e);
                        throw e;
                    }
                } finally {
                    //重置child的标志,在Runnable之后不在使用
                    mIsForkChild = false;
                }
            } else {
                // USAP池事件FD或USAP报告管道。
                //如果这是USAP池事件FD,则有效负载将是已删除的USAP的数量。
                //如果这是USAP报告管道FD,则有效负载将是USAP的PID
                //这只是专门的。

                long messagePayload = -1;

                try {
                    byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES];
                    int readBytes = Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);

                    if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) {
                        DataInputStream inputStream =
                                new DataInputStream(new ByteArrayInputStream(buffer));

                        messagePayload = inputStream.readLong();
                    } else {
                        Log.e(TAG, "Incomplete read from USAP management FD of size "
                                + readBytes);
                        continue;
                    }
                } catch (Exception ex) {
                    if (pollIndex == usapPoolEventFDIndex) {
                        Log.e(TAG, "Failed to read from USAP pool event FD: "
                                + ex.getMessage());
                    } else {
                        Log.e(TAG, "Failed to read from USAP reporting pipe: "
                                + ex.getMessage());
                    }

                    continue;
                }

                if (pollIndex > usapPoolEventFDIndex) {
                    Zygote.removeUsapTableEntry((int) messagePayload);
                }

                usapPoolFDRead = true;
            }
        }

        //检查USAP池是否需要重新填充
        if (usapPoolFDRead) {
            int[] sessionSocketRawFDs =
                    socketFDs.subList(1, socketFDs.size())
                            .stream()
                            .mapToInt(fd -> fd.getInt$())
                            .toArray();

            final Runnable command = fillUsapPool(sessionSocketRawFDs);

            if (command != null) {
                return command;
            }
        }
    }
}

3.3 acceptCommandPeer

[ZygoteServer.java]

private ZygoteConnection acceptCommandPeer(String abiList) {
    try {
        //这个socket是server端的socket,通过accept来接受client传递的数据
        return createNewConnection(mZygoteSocket.accept(), abiList);
    } catch (IOException ex) {
        throw new RuntimeException(
                "IOException during accept()", ex);
    }
}

3.4 createNewConnection

[ZygoteServer.java]

protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
        throws IOException {
    return new ZygoteConnection(socket, abiList);
}

4 ZygoteConnection

4.1 processOneCommand

在之前的ZygoteServer中,创建ZygoteConnection对象,并创建了LocalServerSocket,现在接收到Client端发送来的数据,就会通过processOneCommand来进行处理,这里的处理方法也比较简单,就是读出参数,解析参数,校验,然后fork子进程,最后分别处理父进程和子进程的逻辑

[ZygoteConnection.java]

Runnable processOneCommand(ZygoteServer zygoteServer) {
    String args[];
    ZygoteArguments parsedArgs = null;
    FileDescriptor[] descriptors;

    try {
        //获取Client发送的参数
        args = Zygote.readArgumentList(mSocketReader);
        descriptors = mSocket.getAncillaryFileDescriptors();
    } ...

    //解析参数
    parsedArgs = new ZygoteArguments(args);

    //拿到参数后一系列的校验工作
    ...

    //校验完毕,通过参数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.mTargetSdkVersion);

    try {
        if (pid == 0) {
            //在子进程,处理子进程的相关逻辑
            zygoteServer.setForkChild();

            zygoteServer.closeServerSocket();
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;

            return handleChildProc(parsedArgs, descriptors, childPipeFd,
                    parsedArgs.mStartChildZygote);
        } else {
            //在父进程(Zygote进程),处理父进程的相关逻辑
            IoUtils.closeQuietly(childPipeFd);
            childPipeFd = null;
            handleParentProc(pid, descriptors, serverPipeFd);
            return null;
        }
    } finally {
        IoUtils.closeQuietly(childPipeFd);
        IoUtils.closeQuietly(serverPipeFd);
    }
}

现在子进程也创建完毕了,接下来再看看关于父进程的处理逻辑handleParentProc和子进程的处理逻辑handleChildProc又做了什么

4.2 handleChildProc

[ZygoteConnection.java]

private Runnable handleChildProc(ZygoteArguments parsedArgs, FileDescriptor[] descriptors,
        FileDescriptor pipeFd, boolean isZygote) {

    //子进程会从父进程继承socket,这里进行关闭
    closeSocket();

    ...
        if (!isZygote) {
            //不是zygote进程,所以走这个分支
            return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                    parsedArgs.mRemainingArgs, null /* classLoader */);
        } else {
            return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,
                    parsedArgs.mRemainingArgs, null /* classLoader */);
        }
    }
}

4.3 zygoteInit

关于zygoteInit这个方法,它主要做了三件事

  1. RuntimeInit.commonInit()
  2. ZygoteInit.nativeZygoteInit()
  3. RuntimeInit.applicationInit

这三个方法主要做了一些初始化的操作,在之后的进程启动时会详细介绍,

[ZygoteInit.java]

public static final Runnable zygoteInit(int targetSdkVersion, 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, argv, classLoader);
}

4.4 RuntimeInit.applicationInit

applicationInit这个方法也比较简单,其中最关键是就是最后一行的findStaticMain

[RuntimeInit.java]

protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
        String[] argv, ClassLoader classLoader) {
    ...
    return findStaticMain(args.startClass, args.startArgs, classLoader);
}

4.5 findStaticMain

[RuntimeInit.java]

protected static Runnable findStaticMain(String className, String[] argv,
        ClassLoader classLoader) {
    Class<?> cl;

    try {
        cl = Class.forName(className, true, classLoader);
    } catch (ClassNotFoundException ex) {
        ...
    }

    Method m;
    try {
        m = cl.getMethod("main", new Class[] { String[].class });
    } catch ...

    int modifiers = m.getModifiers();
    if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
        ...
    }

    return new MethodAndArgsCaller(m, argv);
}

4.6 MethodAndArgsCaller

static class MethodAndArgsCaller implements Runnable {
    ...
    public MethodAndArgsCaller(Method method, String[] args) {
        mMethod = method;
        mArgs = args;
    }
    public void run() {
        try {
            mMethod.invoke(null, new Object[] { mArgs });
        } ...
    }
}

五 总结

显然,applicationInit是通过jni的反射获得到一个Java的main方法的Runnable,它实际上就是ActivityThread的main方法,然后最后通过最外层的run来执行这个main方法,这样我们应用的进程就启动了,相对的,在对应的父进程中,zygote会处理一些扫尾工作,等待下一次的fork,到这,关于zygote的工作流程也算是基本告一段落了,当然,关于系统的启动,还远远没有结束,这些在接下来的篇幅说明