应用进程启动流程分析(服务端篇)

我正在参与掘金创作者训练营第4期,点击了解活动详情,一起学习吧!

应用进程启动流程分析(服务端篇)

在上篇应用进程启动流程分析(客户端篇)中,分析到,在Process.start函数调用时,通过跟zygote对应的socket建立对应的连接,然后将参数数据通过socket通信发送给服务端,那么服务端接收到对应的参数,是如何进行处理的呢?

记得此前分析过system_server进程启动流程分析一篇中,ZygoteInit的启动中,我们分析过其main函数(通过app_process程序的main函数中反射调用ZygoteInit.main函数),在其main函数中,会fork system_server进程,但同时会初始化一个ZygoteServer对象

public static void main(String argv[]) {
    ZygoteServer zygoteServer = null;

    /// ......
    Runnable caller;
    try {
        // ......
        // 初始化ZygoteServer对象
        zygoteServer = new ZygoteServer(isPrimaryZygote);

        // ......

        // The select loop returns early in the child process after a fork and
        // loops forever in the zygote.
        caller = zygoteServer.runSelectLoop(abiList);
    }
    // ......
    // We're in the child process and have exited the select loop. Proceed to execute the
    // command.
    if (caller != null) {
        caller.run();
    }
}
复制代码

在ZygoteServer对象初始化之后,会调用其runSelectLoop函数,并返回一个Runnable对象,然后运行它

在runSelectLoop函数中,会启动一个while无限循环,等待Socket连接

Runnable runSelectLoop(String abiList) {
    // ......

    while (true) {
        // ......

        int pollReturnValue;
        try {
            // 遍历轮询所有为pollFDs
            pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
        }
        // .......
        {
            boolean usapPoolFDRead = false;

            while (--pollIndex >= 0) {
                // ......
                // 当此时的pollIndex为0的时候,表明ZygoteServer启动后,有一个客户端来连接
                if (pollIndex == 0) {
                    // 收到客户端连接请求,调用acceptCommandPeer函数初始化一个ZygoteConnection对象
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
                    peers.add(newPeer);
                    socketFDs.add(newPeer.getFileDescriptor());
                // 当前已经至少有一个ZygoteConnection连接建立完成
                } else if (pollIndex < usapPoolEventFDIndex) {
                    // ......
                    try {
                        // 获取对应的ZygoteConnection对象,并调用其processOneCommand函数
                        ZygoteConnection connection = peers.get(pollIndex);
                        final Runnable command = connection.processOneCommand(this);
                        log("runSelectLoop : mIsForkChild = " + mIsForkChild);
                        // 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;
                        }
                    }
                    // ......
                }
            }
            // ......
        }
        // ......
    }
}
复制代码

由上述代码可知,当ZygoteServer初始化后,调用runSelectLoop函数的时候,会启动一个while无限循环,当收到客户端连接请求后,此时通过调用acceptCommandPeer函数,初始化建立链路ZygoteConnection

当收到消息请求时,由于此时的peers中包含有一个ZygoteConnection链接,因此会获取该链接,并调用其processOneCommand函数,并返回一个线程,最终在ZygoteInit.main函数中调用该线程的run函数

亦即

建立一个ZygoteConnection链接

private ZygoteConnection acceptCommandPeer(String abiList) {
    // ......
    return createNewConnection(mZygoteSocket.accept(), abiList);
}

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

也就是说在ZygoteServer中初始化一个ZygoteConnection对象

运行该ZygoteConnection.processOneCommand函数

Runnable processOneCommand(ZygoteServer zygoteServer) {
    String[] args;

    // ......
    // 读取zygote socket传递过来的参数数据
    args = Zygote.readArgumentList(mSocketReader);
    // ......
    // 转化为能够识别的参数数组
    ZygoteArguments parsedArgs = new ZygoteArguments(args);

    // ...... 参数配置

    // 调用Zygote的forkAndSpecialize函数
    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);
    
    // ......
    return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
    // ......
}
复制代码

显然,这段代码,通过Zygote进程fork了一个Launcher应用进程,Zygote.forkAndSpecialize最终会调用Native层的fork函数,在内核进程中fork一个Launcher应用进程 此后经过一系列的操作后,最终会调用ZygoteConnection.handleChildProc函数

private Runnable handleChildProc(ZygoteArguments parsedArgs,
        FileDescriptor pipeFd, boolean isZygote) {
    // ......
    // 关闭当前的SOCKET链接
    closeSocket();
    // 设置进程名称 
    Zygote.setAppProcessName(parsedArgs, TAG);

    //......
    // 通过ZygoteInit对象的zygoteInit函数
    return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
            parsedArgs.mDisabledCompatChanges,
            parsedArgs.mRemainingArgs, null /* classLoader */);
    // ......
}

public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
        String[] argv, ClassLoader classLoader) {
    // ......
    return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
            classLoader);
}

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

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

    try {
        // 找到对应的Class
        cl = Class.forName(className, true, classLoader);
    } catch (ClassNotFoundException ex) {
        throw new RuntimeException(
                "Missing class when invoking static main " + className,
                ex);
    }

    Method m;
    try {
        // 查找对应Class中的main函数
        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);
    }

    // ......
    // 最后返回MethodAndArgsCaller对象
    return new MethodAndArgsCaller(m, argv);
}
复制代码

从上面的流程可知,最后会初始化一个MethodAndArgsCaller对象并返回,从该对象的类图结构

classDiagram
Runnable <|-- MethodAndArgsCaller : 实现
<<interface>>Runnable
RuntimeInit *-- MethodAndArgsCaller : 内部类

返回的是一个实现Runnable接口的对象

梳理一下上述流程,

  1. 在ZygoteInit的main函数中,初始化了一个ZygoteServer对象,
  2. 然后调用其runSelectLoop函数,在这个函数中,会将主线程中实现一个while无限循环,
  3. 在这个无限循环中,一直等待zygote socket的客户端链接,一旦收到客户端的链接请求,则通过acceptCommandPeer函数初始化一个ZygoteConnection链路,然后调用该链路的processOneCommand函数,
  4. 通过JNI函数在内核空间中fork一个应用进程,,并最终通过一系列进程调度操作,调用了ZygoteConnection的handleChildProc,并最终返回一个MethodAndArgsCaller对象, MethodAndArgsCaller对象是一个实现Runnable接口的对象

运行上述得到的MethodAndArgsCaller对象

再回到ZygoteInit的main函数中可以看到,runSelectLoop函数返回的MethodAndArgsCaller不为null,则运行这个Runnable接口线程

ZygoteInit.main
if (caller != null) {
    caller.run();
}

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() {
        // ......
        mMethod.invoke(null, new Object[] { mArgs });
        // ......
    }
}
复制代码

根据此前的分析,此处的MethodAndArgsCaller的mMethod参数对应的是android.os.ActivityThread类的main函数,然后通过反射调用这个函数

public static void main(String[] args) {
    // ......

    Environment.initForCurrentUser();

    // Make sure TrustedCertificateStore looks in the right place for CA certificates
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);

    // Call per-process mainline module initialization.
    initializeMainlineModules();

    Process.setArgV0("<pre-initialized>");

    Looper.prepareMainLooper();

    // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
    // It will be in the format "seq=114"
    long startSeq = 0;
    if (args != null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                startSeq = Long.parseLong(
                        args[i].substring(PROC_START_SEQ_IDENT.length()));
            }
        }
    }
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    // ......
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}
复制代码

最终在ActivityThread对象中启动对应的应用的具体Activity,这个我们等后续,再行更新

流程时序图

图片.png

Guess you like

Origin juejin.im/post/7067533314260893726