理解Android进程创建流程

Posted 码农乐园

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解Android进程创建流程相关的知识,希望对你有一定的参考价值。

进程:每个App在启动前必须先创建一个进程,该进程是由Zygote fork出来的,进程具有独立的资源空间,用于承载App上运行的各种Activity/Service等组件。进程对于上层应用来说是完全透明的,这也是google有意为之,让App程序都是运行在android Runtime。大多数情况一个App就运行在一个进程中,除非在AndroidManifest.xml中配置Android:process属性,或通过native代码fork进程。

线程:线程对应用开发者来说非常熟悉,比如每次new Thread().start()都会创建一个新的线程,该线程并没有自己独立的地址空间,而是与其所在进程之间资源共享。从Linux角度来说进程与线程都是一个task_struct结构体,除了是否共享资源外,并没有其他本质的区别。

在接下来的文章,会涉及到system_server进程和Zygote进程,下面简要这两个进程:

  • system_server进程:是用于管理整个Java framework层,包含ActivityManager,PowerManager等各种系统服务;

  • Zygote进程:是Android系统的首个Java进程,Zygote是所有Java进程的父进程,包括 system_server进程以及所有的App进程都是Zygote的子进程,注意这里说的是子进程,而非子线程。

如果想更进一步了解system_server进程和Zygote进程在整个Android系统所处的地位,可查看我的另一个文章Android系统-开篇。

进程创建图

对于大多数的应用开发者来说创建线程比较熟悉,而对于创建进程并没有太多的概念。对于系统工程师或者高级开发者,还是有很必要了解Android系统是如何一步步地创建出一个进程的。先来看一张进程创建过程的简要图:

图解:

  1. App发起进程:当从桌面启动应用,则发起进程便是Launcher所在进程;当从某App内启动远程进程,则发送进程便是该App所在进程。发起进程先通过binder发送消息给system_server进程;

  2. system_server进程:调用Process.start()方法,通过socket向zygote进程发送创建新进程的请求;

  3. zygote进程:在执行ZygoteInit.main()后便进入runSelectLoop()循环体内,当有客户端连接时便会执行ZygoteConnection.runOnce()方法,再经过层层调用后fork出新的应用进程;

  4. 新进程:执行handleChildProc方法,最后调用ActivityThread.main()方法。

接下来,依次从system_server进程发起请求到Zygote创建进程,再到新进程的运行这3大块展开讲解进程创建是一个怎样的过程。

二. system_server发起请求

1. Process.start

[-> Process.java]

public static final ProcessStartResult start(final String processClass, final String niceName, int uid, int gid, int[] gids, int debugFlags, int mountExternal, int targetSdkVersion, String seInfo, String abi, String instructionSet, String appDataDir, String[] zygoteArgs) {
    try {
         //【见小节2】
        return startViaZygote(processClass, niceName, uid, gid, gids,
                debugFlags, mountExternal, targetSdkVersion, seInfo,
                abi, instructionSet, appDataDir, zygoteArgs);
    } catch (ZygoteStartFailedEx ex) {
        throw new RuntimeException("");
    }
}

2. startViaZygote

[-> Process.java]

private static ProcessStartResult startViaZygote(final String processClass, final String niceName, final int uid, final int gid, final int[] gids, int debugFlags, int mountExternal, int targetSdkVersion, String seInfo, String abi, String instructionSet, String appDataDir, String[] extraArgs) throws ZygoteStartFailedEx {
    synchronized(Process.class) {
        ArrayList<String> argsForZygote = new ArrayList<String>();

        argsForZygote.add("--runtime-args");
        argsForZygote.add("--setuid=" + uid);
        argsForZygote.add("--setgid=" + gid);
        argsForZygote.add("--target-sdk-version=" + targetSdkVersion);

        if (niceName != null) {
            argsForZygote.add("--nice-name=" + niceName);
        }
        if (appDataDir != null) {
            argsForZygote.add("--app-data-dir=" + appDataDir);
        }
        argsForZygote.add(processClass);

        if (extraArgs != null) {
            for (String arg : extraArgs) {
                argsForZygote.add(arg);
            }
        }
         //【见小节3】
        return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
    }
}

该过程主要工作是生成argsForZygote数组,该数组保存了进程的uid、gid、groups、target-sdk、nice-name等一系列的参数。

3. zygoteSendArgsAndGetResult

[-> Process.java]

private static ProcessStartResult zygoteSendArgsAndGetResult( ZygoteState zygoteState, ArrayList<String> args) throws ZygoteStartFailedEx {
    try {
        //其中zygoteState 【见小节3.1】
        final BufferedWriter writer = zygoteState.writer;
        final DataInputStream inputStream = zygoteState.inputStream;

        writer.write(Integer.toString(args.size()));
        writer.newLine();

        int sz = args.size();
        for (int i = 0; i < sz; i++) {
            String arg = args.get(i);
            if (arg.indexOf('\\n') >= 0) {
                throw new ZygoteStartFailedEx(
                        "embedded newlines not allowed");
            }
            writer.write(arg);
            writer.newLine();
        }

        writer.flush();

        ProcessStartResult result = new ProcessStartResult();
        //等待socket服务端(即zygote)返回新创建的进程pid;
        //对于等待时长问题,Google正在考虑此处是否应该有一个timeout,但目前是没有的。
        result.pid = inputStream.readInt();
        if (result.pid < 0) {
            throw new ZygoteStartFailedEx("fork() failed");
        }
        result.usingWrapper = inputStream.readBoolean();
        return result;
    } catch (IOException ex) {
        zygoteState.close();
        throw new ZygoteStartFailedEx(ex);
    }
}

这个方法的主要功能是通过socket通道向Zygote进程发送一个参数列表,然后进入阻塞等待状态,直到远端的socket服务端发送回来新创建的进程pid才返回。

3.1 openZygoteSocketIfNeeded

private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
    if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
        try {
            //向主zygote发起connect()操作
            primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);
        } catch (IOException ioe) {
            ...
        }
    }

    if (primaryZygoteState.matches(abi)) {
        return primaryZygoteState;
    }

    if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
        //当主zygote没能匹配成功,则采用第二个zygote,发起connect()操作
        secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET);
    }

    if (secondaryZygoteState.matches(abi)) {
        return secondaryZygoteState;
    }
    ...
}

openZygoteSocketIfNeeded(abi)方法是根据当前的abi来选择与zygote还是zygote64来进行通信。

三. Zygote创建进程

文章Android系统启动-zygote篇已介绍,简单来说就是Zygote进程是由由init进程而创建的,进程启动之后调用ZygoteInit.main()方法,经过创建socket管道,预加载资源后,便进程runSelectLoop()方法。

4. ZygoteInit.main

[–>ZygoteInit.java]

public static void main(String argv[]) {
    try {
        runSelectLoop(abiList); //【见小节5】
        ....
    } catch (MethodAndArgsCaller caller) {
        caller.run(); //【见小节16】
    } catch (RuntimeException ex) {
        closeServerSocket();
        throw ex;
    }
}

后续会讲到runSelectLoop()方法会抛出异常MethodAndArgsCaller,从而进入caller.run()方法。

5. runSelectLoop

[-> ZygoteInit.java]

private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
    //sServerSocket是socket通信中的服务端,即zygote进程。保存到fds[0]
    fds.add(sServerSocket.getFileDescriptor());
    peers.add(null);

    while (true) {
        StructPollfd[] pollFds = new StructPollfd[fds.size()];
        for (int i = 0; i < pollFds.length; ++i) {
            pollFds[i] = new StructPollfd();
            pollFds[i].fd = fds.get(i);
            pollFds[i].events = (short) POLLIN;
        }
        try {
             //处理轮询状态,当pollFds有事件到来则往下执行,否则阻塞在这里
            Os.poll(pollFds, -1);
        } catch (ErrnoException ex) {
            ...
        }

        for (int i = pollFds.length - 1; i >= 0; --i) {
            //采用I/O多路复用机制,当接收到客户端发出连接请求 或者数据处理请求到来,则往下执行;
            // 否则进入continue,跳出本次循环。
            if ((pollFds[i].revents & POLLIN) == 0) {
                continue;
            }
            if (i == 0) {
                //即fds[0],代表的是sServerSocket,则意味着有客户端连接请求;
                // 则创建ZygoteConnection对象,并添加到fds。//【见小节5.1】
                ZygoteConnection newPeer = acceptCommandPeer(abiList);
                peers.add(newPeer);
                fds.add(newPeer.getFileDesciptor()); //添加到fds.
            } else {
                //i>0,则代表通过socket接收来自对端的数据,并执行相应操作【见小节6】
                boolean done = peers.get(i).runOnce();
                if (done) {
                    peers.remove(i);
                    fds.remove(i); //处理完则从fds中移除该文件描述符
                }
            }
        }
    }
}

该方法主要功能:

  • 客户端通过openZygoteSocketIfNeeded()来跟zygote进程建立连接。zygote进程收到客户端连接请求后执行accept();然后再创建ZygoteConnection对象,并添加到fds数组列表;

  • 建立连接之后,可以跟客户端通信,进入runOnce()方法来接收客户端数据,并执行进程创建工作。

5.1 acceptCommandPeer

[-> ZygoteInit.java]

private static ZygoteConnection acceptCommandPeer(String abiList) {
    try {
        return new ZygoteConnection(sServerSocket.accept(), abiList);
    } catch (IOException ex) {
        ...
    }
}

接收客户端发送过来的connect()操作,Zygote作为服务端执行accept()操作。再后面客户端调用write()写数据,Zygote进程调用read()读数据。

没有连接请求时会进入休眠状态,当有创建新进程的连接请求时,唤醒Zygote进程,创建Socket通道ZygoteConnection,然后执行ZygoteConnection的runOnce()方法。

6. runOnce

[-> ZygoteConnection.java]

boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {

    String args[];
    Arguments parsedArgs = null;
    FileDescriptor[] descriptors;

    try {
        //读取socket客户端发送过来的参数列表
        args = readArgumentList();
        descriptors = mSocket.getAncillaryFileDescriptors();
    } catch (IOException ex) {
        closeSocket();
        return true;
    }

    PrintStream newStderr = null;
    if (descriptors != null && descriptors.length >= 3) {
        newStderr = new PrintStream(new FileOutputStream(descriptors[2]));
    }

    int pid = -1;
    FileDescriptor childPipeFd = null;
    FileDescriptor serverPipeFd = null;

    try {
        //将binder客户端传递过来的参数,解析成Arguments对象格式
        parsedArgs = new Arguments(args);
        ...

        int [] fdsToClose = { -1, -1 };
        FileDescriptor fd = mSocket.getFileDescriptor();
        if (fd != null) {
            fdsToClose[0] = fd.getInt$();
        }

        fd = ZygoteInit.getServerSocketFileDescriptor();
        if (fd != null) {
            fdsToClose[1] = fd.getInt$();
        }
        fd = null;
        //【见小节7】
        pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
                parsedArgs.appDataDir);
    } catch (Exception e) {
        ...
    }

    try {
        if (pid == 0) {
            //子进程执行
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;
            //【见小节13】
            handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);

            // 不应到达此处,子进程预期的是抛出异常ZygoteInit.MethodAndArgsCaller或者执行exec().
            return true;
        } else {
            //父进程执行
            IoUtils.closeQuietly(childPipeFd);
            childPipeFd = null;
            return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
        }
    } finally {
        IoUtils.closeQuietly(childPipeFd);
        IoUtils.closeQuietly(serverPipeFd);
    }
}

7. forkAndSpecialize

[-> Zygote.java]

public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, String instructionSet, String appDataDir) {
    VM_HOOKS.preFork(); //【见小节8】
    int pid = nativeForkAndSpecialize(
              uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
              instructionSet, appDataDir); //【见小节9】
    ...
    VM_HOOKS.postForkCommon(); //【见小节11】
    return pid;
}

VM_HOOKS是Zygote对象的静态成员变量:VM_HOOKS = new ZygoteHooks();

7.1 Zygote进程

先说说Zygote进程,如下图:

从图中可知Zygote进程有4个Daemon子线程分别是ReferenceQueueDaemon,FinalizerDaemon,FinalizerWatchdogDaemon,HeapTaskDaemon。图中线程名显示的并不完整是由于底层的进程结构体task_struct是由长度为16的char型数组保存,超过15个字符便会截断。

可能有人会问zygote64进程不是还有system_server,com.android.phone等子线程,怎么会只有4个呢?那是因为这些并不是Zygote子线程,而是Zygote的子进程。在图中用红色圈起来的是进程的VSIZE,virtual size),代表的是进程虚拟地址空间大小。线程与进程的最为本质的区别便是是否共享内存空间,图中VSIZE和Zygote进程相同的才是Zygote的子线程,否则就是Zygote的子进程。

8. preFork

[-> ZygoteHooks.java]

 public void preFork() {
    Daemons.stop(); //停止4个Daemon子线程【见小节8.1】
    waitUntilAllThreadsStopped(); //等待所有子线程结束【见小节8.2】
    token = nativePreFork(); //完成gc堆的初始化工作【见小节8.3】
}

8.1 Daemons.stop

public static void stop() {
    HeapTaskDaemon.INSTANCE.stop(); //Java堆整理线程
    ReferenceQueueDaemon.INSTANCE.stop(); //引用队列线程
    FinalizerDaemon.INSTANCE.stop(); //析构线程
    FinalizerWatchdogDaemon.INSTANCE.stop(); //析构监控线程
}

此处守护线程Stop方式是先调用目标线程interrrupt()方法,然后再调用目标线程join()方法,等待线程执行完成。

8.2 waitUntilAllThreadsStopped

private static void waitUntilAllThreadsStopped() {
    File tasks = new File("/proc/self/task");
    // 当/proc中线程数大于1,就出让CPU直到只有一个线程,才退出循环
    while (tasks.list().length > 1) {
        Thread.yield();
    }
}

8.3 nativePreFork

nativePreFork通过JNI最终调用如下方法:

[-> dalvik_system_ZygoteHooks.cc]

static jlong ZygoteHooks_nativePreFork(JNIEnv* env, jclass) {
    Runtime* runtime = Runtime::Current();
    runtime->PreZygoteFork(); // 见下文
    if (Trace::GetMethodTracingMode() != TracingMode::kTracingInactive) {
      Trace::Pause();
    }
    //将线程转换为long型并保存到token,该过程是非安全的
    return reinterpret_cast<jlong>(ThreadForEnv(env));
}

至于runtime->PreZygoteFork的过程:

void Runtime::PreZygoteFork() {
    // 堆的初始化工作。这里就不继续再往下追art虚拟机
    heap_->PreZygoteFork();
}

VM_HOOKS.preFork()的主要功能便是停止Zygote的4个Daemon子线程的运行,等待并确保Zygote是单线程(用于提升fork效率),并等待这些线程的停止,初始化gc堆的工作, 并将线程转换为long型并保存到token

以上是关于理解Android进程创建流程的主要内容,如果未能解决你的问题,请参考以下文章

源码分析——从AIDL的使用开始理解Binder进程间通信的流程

Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段

Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段

Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段

Android 逆向Android 进程注入工具开发 ( Visual Studio 开发 Android NDK 应用 | Visual Studio 中 SDK 和 NDK 安装位置 )(代码片段

android adb 流程原理代码分析