当前位置:   article > 正文

android zygote 进程分析_安卓 preloadclass 注入

安卓 preloadclass 注入

1. 概述

在 Android 系统中,所有的应用程序以及系统服务进程 SystemService 都是由 zygote 进程孕育 (fork)出来的。zygote 进程的作用可以概括为以下三点:

  1. 创建 java 虚拟机,加载系统资源
  2. 应用程序启动过程中负责 fork 出子进程
    在 Android 应用程序启动时,ActivityManagerService 会通过 socket 与 zygote 进程进行通信,请求它 fork 一个子进程出来作为这个即将要启动的应用程序的进程。
  3. 系统重要服务进程 SystemService 的孕育者
    Android 系统中非常重要的系统服务进程 SystemServer 也是 zygote 在启动过程中 fork 出来的。

2. zygote 分析

2.1 启动过程分析

我们知道 Android 系统是基于 Linux 内核的,而在 Linux 系统中,所有的进程都是 init 进程的子进程,zygote 进程也不例外,它是由 init 进程创建的。

File:system/core/rootdir/init.rc

import /init.${ro.zygote}.rc
  • 1
  • 2
  • 3
File:system/core/rootdir/init.zygote32.rc

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  1. 关键字 service 为:创建一个名为 zygote 的进程,这个 zygote 进程要执行的程序是 /system/bin/app_process,后面是传给 app_process 的参数。
  2. socket 关键字表示这个 zygote 进程需要一个名称为 zygote 的 socket 资源,这样系统在我们就可以在 /dev/socket 目录下看到一个名为 zygote 的文件,这里的 socket 主要用于本地进程间通信。ActivityManagerService 就是通这个 socket 来和 zygote 进程通信请求 fork 一个应用程序进程。
  3. 后面的 onrestart 表示这个 zygote 进程重启时需要执行的命令。

而进程 app_process 的源码位置在:platform/frameworks/base/cmds/app_process/app_main.cpp

这个函数由两种启动模式:

  • 一种是 zygote 模式,也就是初始化 zygote 进程,传递的参数有 --start-system-server、 --socket-name=zygote,前者表示启动SystemServer,后者指定socket的名称
  • 一种是 application 模式,也就是启动普通应用程序,传递的参数有 class 名字以及 class 带的参数

两者最终都是调用 AppRuntime 对象的 start 函数,加载 ZygoteInit 或 RuntimeInit 两个Java类,并将之前整理的参数传入进去。

由于本篇讲的是zygote进程启动流程,因此接下来我只讲解ZygoteInit的加载.

int main(int argc, char* const argv[])
{
    ...

    // 构建 AppRuntime 对象,并将参数 (-Xzygote) 传递进去
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    // Process command line arguments
    // ignore argv[0]
    argc--;
    argv++;
    
    // 所有在"--"后面或者非"-"开头的参数都会被传入vm
    
    // 所谓一个特例,在 spaced_commands 变量中定义的参数会被传入vm
    const char* spaced_commands[] = { "-cp", "-classpath" };
    // Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
    bool known_command = false;
    
    ...
    省略代码
    通过 runtime.addOption 初始化参数集合
    这边直接跳过
    ...

     // Parse runtime arguments.  Stop at first unrecognized option.
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

    // 跳过一个参数,之前跳过了 -Xzygote,这里继续跳过 /system/bin,就是所谓的 parent dir
    ++i;  // Skip unused "parent dir" argument.
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) { // 表示是 zygote 启动模式
            zygote = true;
            niceName = ZYGOTE_NICE_NAME; // 根据平台可能是 zygote64 或 zygote
        } else if (strcmp(arg, "--start-system-server") == 0) { // 是否开启 SystemService
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) { // application 启动模式
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) { // 进程别名
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) { // application启动的class
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }

    Vector<String8> args;
    if (!className.isEmpty()) {
        // className 不为空表示是 application 启动模式
        // 这里不做分析
        ...
    } else {
        // 表示处在 zygote 启动模式
        // 新建Dalvik的缓存目录.
        maybeCreateDalvikCache();

        if (startSystemServer) {
            args.add(String8("start-system-server"));
        }

        //加入--abi-list=参数
        char prop[PROP_VALUE_MAX];
        if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
            LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
                ABI_LIST_PROPERTY);
            return 11;
        }

        String8 abiFlag("--abi-list=");
        abiFlag.append(prop);
        args.add(abiFlag);

        // In zygote mode, pass all remaining arguments to the zygote
        // main() method.
        for (; i < argc; ++i) {
            args.add(String8(argv[i]));
        }
    }

    // 设置进程别名
    if (!niceName.isEmpty()) {
        runtime.setArgv0(niceName.string(), true /* setProcName */);
    }

    if (zygote) { 
        //如果是zygote启动模式,则加载ZygoteInit
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102

我们看到,在最后调用的是 runtime.start 函数,这个就是要启动虚拟机了,接下来我们分析 start 函数

2.2 AppRuntime 分析

AppRuntime 类的声明和实现均在 app_main.cpp 中,它是由 AndroidRuntime 类派生出来的,AppRuntime 重载了 onStart、onZygoteInit、onExit 等一些重要的方法。

前面调用的 start 方法实现在基类 AndroidRuntime 中,前半部分主要是初始化JNI,然后创建虚拟机,注册一些JNI函数

File:platform/frameworks/base/core/jni/AndroidRuntime.cpp

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ... // 日志打印,获取ANDROID_ROOT环境变量

    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    // ① 创建虚拟机
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env); //表示虚拟创建完成时回调的方法
    
    /*
     * Register android functions.
     */
     // ② 注册 JNI 函数
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
    
    ... //③ JNI 方式调用 ZygoteInit 类的 main 函数
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

上面的分析中,关键点①、②、③共同组成了 Android 系统中 Java 世界的三部曲。

1. 创建虚拟机 startVm

这个函数没有特别之处,就是调用JNI的虚拟机创建函数,但是创建虚拟机时的一些参数是在 startVm 中确定的,其实就是从各种系统属性中读取一些参数,然后通过 addOption 设置到 AndroidRuntime 的 mOptions 数组中存起来,代码如下:

File:platform/frameworks/base/core/jni/AndroidRuntime.cpp

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{

    // 调用 JNI_CreateJavaVM 创建虚拟机,pEnv 返回当前线程的 JniEnv 变量
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
        ALOGE("JNI_CreateJavaVM failed\n");
        return -1;
    }

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
2. 注册 JNI 函数 startReg

前面是如何创建虚拟机,下一步需要给虚拟机注册一些 JNI 函数,正式因为后续 Java 世界用到的一些函数时采用 native 方法实现的,所以才必须提前注册这些函数

File:platform/frameworks/base/core/jni/AndroidRuntime.cpp

/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
    ATRACE_NAME("RegisterAndroidNatives");
    
    // 设置 Thread 类的线程创建函数为 javaCreateThreadEtc
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);

    ALOGV("--- registering native functions ---\n");

    // 创建一个 200 容量的局部引用作用域
    env->PushLocalFrame(200);

    // 注册 Jni 函数
    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        env->PopLocalFrame(NULL);
        return -1;
    }
    env->PopLocalFrame(NULL); //释放局部引用作用域

    //createJavaThread("fubar", quickTest, (void*) "hello");

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

register_jni_procs是主要的 JNI 注册函数

static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
    for (size_t i = 0; i < count; i++) {
        if (array[i].mProc(env) < 0) {
#ifndef NDEBUG
            ALOGD("----------!!! %s failed to load\n", array[i].mName);
#endif
            return -1;
        }
    }
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

mProc 就是为 Java 类注册了 JNI 函数

3. 反射调用 ZygoteInit 类的 main 函数

虚拟机已创建好,JNI函数也已注册,下一步就是通过CallStaticVoidMethod了,通过这个函数真正进入 Java 世界。

这个 Java 世界的入口就是 CallStaticVoidMethod 最终调用的 com.android.internal.os.ZygoteInit 的 main 函数。

public static void main(String argv[]) {
    ZygoteServer zygoteServer = new ZygoteServer();
    
    ...
    
    // ① 创建 Socket,通过这个 Socket 与 ActivityManagerService 通信
    zygoteServer.registerServerSocket(socketName);
    ...
    // ② 预加载资源
    preload(bootTimingsTraceLog);
    ...
    
    if (startSystemServer) {
        // ③ fork 出 SystemService
        Runnable r = forkSystemServer(abiList, socketName, zygoteServer);

        // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
        // child (system_server) process.
        if (r != null) {
            r.run();
            return;
        }
    }
    
    ......
    zygoteServer.runSelectLoop(abiList);
    ......
}   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
建立 IPC 通信服务端 - registerServerSocket

先分析主体的框架,首先创建了一个 ZygoteServer 类,并 registerServerSocket 创建一个 Socket,这个Socket 将 listen 并 accept Client

它将作为服务端来和客户端的 ActivityManagerService 进行通信来 fork 出子进程。

预加载类和资源 - preload

首先看 preloadClass

File:ZygoteInit.java

private static void preloadClasses() {
        final VMRuntime runtime = VMRuntime.getRuntime();

        InputStream is;
        try {
            // 预加载类的信息存储在 PRELOADED_CLASSED 中(/system/etc/preloaded-classes)
            is = new FileInputStream(PRELOADED_CLASSES);
        } catch (FileNotFoundException e) {
            Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
            return;
        }

        ... // 统计工作
        
        try {
            BufferedReader br
                = new BufferedReader(new InputStreamReader(is), 256);

            int count = 0;
            String line;
            while ((line = br.readLine()) != null) {
                // Skip comments and blank lines.
                line = line.trim();
                if (line.startsWith("#") || line.equals("")) {
                    continue;
                }

                Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
                try {
                    if (false) {
                        Log.v(TAG, "Preloading " + line + "...");
                    }
                    // Load and explicitly initialize the given class. Use
                    // Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups
                    // (to derive the caller's class-loader). Use true to force initialization, and
                    // null for the boot classpath class-loader (could as well cache the
                    
                    // 通过 JAVA 反射来加载类
                    // class-loader of this class in a variable).
                    Class.forName(line, true, null);
                    count++;
                } catch (ClassNotFoundException e) {
                }
            }

            Log.i(TAG, "...preloaded " + count + " classes in "
                    + (SystemClock.uptimeMillis()-startTime) + "ms.");
        } catch (IOException e) {
            Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
        } finally {
           ... // 扫尾工作
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

preload_class 文件由 framework/base/tools/preload 工具生成,它判断每个类加载的时间是否大于1250微秒,超过就写入 preload_class 文件,最后由 zygote 进行预加载。

preloadClass 执行的时间比较长,这也是andorid启动慢的原因,可以在此进行开机速度优化

preloadResource 预加载的原理很简单,就是在 zygote 进程启动后将资源读取出来,保存到 Resources 一个全局静态变量中,下次读取系统资源的时候优先从静态变量中查找

启动 SystemService —— forkSystemServer

第三个关键点是 startSystemService,这个函数会创建 Java 世界中传统 Service 所驻留的进程 system_service,该进程是 framework 的核心。

 private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        ...
        
        // 设置参数
        /* Hardcoded command line to start the system server */
        String args[] = {
            "--setuid=1000",
            "--setgid=1000",
            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,3001,3002,3003,3006,3007,3009,3010",
            "--capabilities=" + capabilities + "," + capabilities,
            "--nice-name=system_server",
            "--runtime-args",
            "com.android.server.SystemServer",
        };
        ZygoteConnection.Arguments parsedArgs = null;

        int pid;

        try {
            parsedArgs = new ZygoteConnection.Arguments(args);
            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

            /* Request to fork the system server process */
            // fork 出一个子进程,这个子进程就是 systemSevice
            pid = Zygote.forkSystemServer(
                    parsedArgs.uid, parsedArgs.gid,
                    parsedArgs.gids,
                    parsedArgs.debugFlags,
                    null,
                    parsedArgs.permittedCapabilities,
                    parsedArgs.effectiveCapabilities);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }

        /* For child process */
        if (pid == 0) {
            // fork 成功,开启一些预备工作
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }

            zygoteServer.closeServerSocket();
            return handleSystemServerProcess(parsedArgs);
        }

        return null;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

Zygote.forkSystemServer() 创建一个系统服务进程,进程创建成功后返回 pid 为 0,由于此处生成的新进程和 Zygote 进程一模一样,也就是说这个新进程中同样包含了刚才创建的 Socket,但是该 Socket 在此处无效,因此要将其关闭。接下来调用 handleSystemServerProcess() 处理刚才新建的进程即 SystemServer 进程,需要注意此时已经工作在 SystemServe r进程中

有求必应之等待请求 —— runSelectLoop

前面一个关键点是 registZygotSocket 注册了一个用于 IPC 的 Socket,它的用途在这个函数中能体现出来,这个函数监听客户端的请求

/**
 * Runs the zygote process's select loop. Accepts new connections as
 * they happen, and reads commands from connections one spawn-request's
 * worth at a time.
 */
Runnable runSelectLoop(String abiList) {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

    fds.add(mServerSocket.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 {
            Os.poll(pollFds, -1);
        } catch (ErrnoException ex) {
            throw new RuntimeException("poll failed", ex);
        }
        for (int i = pollFds.length - 1; i >= 0; --i) {
            if ((pollFds[i].revents & POLLIN) == 0) {
                continue;
            }

            if (i == 0) {
                // 新客户端连接请求,充当服务端 Socket
                ZygoteConnection newPeer = acceptCommandPeer(abiList);
                peers.add(newPeer);
                fds.add(newPeer.getFileDesciptor());
            } else {
                try {
                    ZygoteConnection connection = peers.get(i);
                    final Runnable command = connection.processOneCommand(this);

                    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(i);
                            fds.remove(i);
                        }
                    }
                } catch (Exception e) {
                   ...
                }
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 处理客户端连接和客户请求,其中客户在 zygote 中用 ZygoteConnection 对象来表示
  • 客户的请求在函数 processOneCommand 中处理

3. 总结

zygote 是 Android系统中创建 Java 世界的盘古,它创建第一个 Java 虚拟机,同时它又是女娲,繁殖了 framework 的核心 system_server 进程

  1. 第一天,创建 AppRuntime 对象,并调用他的 start,此后的活动由 AppRuntime 控制
  2. 第二天,调用 startVm 创建 Java 虚拟机,然后调用 startReg 来注册 JNI 函数
  3. 第三天,通过 JNI 调用 com.android.internal.os.ZygoteInit 类的 main 函数,进入 Java 世界,但这个世界刚开始啥也没有
  4. 第四天,调用 registerZygoteSocket,通过这个函数,它可以响应子孙后代的请求,同时 zygote 调用 preloadClasses 和 preloadResources,为Java世界添砖加瓦
  5. 第五天,zygote 觉得自己的工作压力大了,便通过调用 startSystemServer 分裂一个子进程 system_server 来为 java 世界服务
  6. 第六天,zygote 调用 runSelectLoop – 等待并处理来自客户的消息,后便睡去

以后的日子里,zygote 随时守护在我们周围,当接收子孙后代的请求后,它会随时醒来为它们工作。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/227942
推荐阅读
相关标签
  

闽ICP备14008679号