Android 系统启动之 zygote 进程启动分析一

前面我们说到,init 进程会解析 init.rc 文件,执行对应的命令,其中一个重要的任务就是启动 Zygote 进程。接下来我们就来分析 Zygote 进程的启动和执行过程。

本文基于 AOSP android-10.0.0_r41 版本讲解

Zygote 进程的启动

system/core/rootdir/init.rc 中有如下定义

bash 复制代码
# system/core/rootdir/init.rc

on late-init
    # ......
    trigger zygote-start
    # ......

这里是一个 Action,当 late-init trigger 触发时,主动调用 trigger 命令触发 zygote-start Action。

bash 复制代码
# system/core/rootdir/init.rc

on zygote-start && property:ro.crypto.state=unencrypted
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start netd
    start zygote
    start zygote_secondary

on zygote-start && property:ro.crypto.state=unsupported
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start netd
    start zygote
    start zygote_secondary

on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start netd
    start zygote
    start zygote_secondary

system/core/rootdir/init.rc 中有三个 zygote 相关的 zygote-start,差异主要在后续的属性值判断,无论哪种情况,都会执行 start zygotestart zygote_secondary.

从 Android 5.0 开始,Android 开始支持 64 位编译,Zygote 本身也会有 32 位和 64 位的区别,因此,通过 ro.zygote 属性来控制启动不同版本的 Zygote 进程:

bash 复制代码
# system/core/rootdir/init.rc
import /init.${ro.zygote}.rc

在 android-10.0.0_r41 的源码中,有 4 个文件定义有 zygote-start:

bash 复制代码
system/core/rootdir/init.zygote32.rc
system/core/rootdir/init.zygote64.rc
system/core/rootdir/init.zygote32_64.rc
system/core/rootdir/init.zygote64_32.rc

我们看下 system/core/rootdir/init.zygote64_32.rc:

bash 复制代码
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary 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

service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote_secondary stream 660 root system
    socket usap_pool_secondary stream 660 root system
    onrestart restart zygote
    writepid /dev/cpuset/foreground/tasks
  • 从文件内容中可以看到定义了两个 Zygote 服务:zygote 和 zygote_secondary
  • 两个服务最大的区别是可执行文件不同:一个是 app_process32、一个是 app_process64
  • init.zygote32_64.rc把两个可执行文件交换一下
  • 对于 init.zygote32.rc 和 init.zygote64.rc 分别只有一个 Zygote 服务

app_process 的用法

在上一节 init 进程调用 app_process 启动 zygote 进程,app_process 是一个 cpp 的命令行程序,接下来我们来看看该命令的使用方法:

bash 复制代码
app_process [java-options] cmd-dir start-class-name [options]

接着 zygote 启动的命令:

bash 复制代码
 /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
  • -Xzygote 属于 [java-options],这些参数会传给虚拟机,并且参数必须以 - 开头,一旦遇到非 - 或者 --,表示 [java-options] 结束
  • /system/bin :属于 cmd-dir,程序的运行目录,随便指定即可,文件操作时会以此为当前路径,大多运行在 /system/bin
  • --zygote --start-system-server --socket-name=zygote 都属于 [options],这些参数都以符号--开头。参数 --zygote 表示要启动 Zygote 进程,--start-system-server 表示要启动 SystemServer,--socket-name=zygote 用于指定 Zygote 中 socket 服务的名字

我们再看一个启动 Java 程序的命令:

bash 复制代码
app_process -Djava.class.path=/data/local/tmp/Test.dex /data/lee Test
  • -Djava.class.p**:属于 [java-options],这些参数会传给虚拟机,并且参数必须以 - 开头,一旦遇到非 - 或者 --,表示 [java-options] 结束
  • /data/lee:属于 cmd-dir,程序的运行目录,随便指定即可,文件操作时会以此为当前路径,大多运行在 /system/bin 下
  • Test:属于 start-class-name,声明入口类的名称,有包名的话需要加上包名

app_process 源码分析

app_process 可执行文件对应的源码在 frameworks/base/cmds/app_process/app_main.cpp

cpp 复制代码
// frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
    // ......
    // 创建 AppRuntime 对象
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    argc--;
    argv++;

这里的主要任务是创建 AppRuntime 对象,AndroidRuntime类是底层很重要很重要的一个类,主要作用是创建和初始化虚拟机。

cpp 复制代码
    // 解析 Java-Option 参数
    const char* spaced_commands[] = { "-cp", "-classpath" };
    bool known_command = false;
    int i;
    for (i = 0; i < argc; i++) {
        if (known_command == true) {
          runtime.addOption(strdup(argv[i]));
          // The static analyzer gets upset that we don't ever free the above
          // string. Since the allocation is from main, leaking it doesn't seem
          // problematic. NOLINTNEXTLINE
          ALOGV("app_process main add known option '%s'", argv[i]);
          known_command = false;
          continue;
        }

        for (int j = 0;
             j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
             ++j) {
          if (strcmp(argv[i], spaced_commands[j]) == 0) {
            known_command = true;
            ALOGV("app_process main found known command '%s'", argv[i]);
          }
        }

        if (argv[i][0] != '-') {
            break;
        }
        if (argv[i][1] == '-' && argv[i][2] == 0) {
            ++i; // Skip --.
            break;
        }

        runtime.addOption(strdup(argv[i]));
        // The static analyzer gets upset that we don't ever free the above
        // string. Since the allocation is from main, leaking it doesn't seem
        // problematic. NOLINTNEXTLINE
        ALOGV("app_process main add option '%s'", argv[i]);
    }

这里把 Java-Option 参数从命令行中解析出来,并把参数添加到 AppRuntime

cpp 复制代码
    // 需要解析出的参数
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

    ++i;  // Skip unused "parent dir" argument.
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }

这里把 Option 参数命令行中解析出来,保存在 zygote startSystemServer 等局部变量中。

cpp 复制代码
    Vector<String8> args; //ZygoteInit 或者 RuntimeInit 启动需要的参数存在 Vector 中
    if (!className.isEmpty()) { //RuntimeInit
        // We're not in zygote mode, the only argument we need to pass
        // to RuntimeInit is the application argument.
        //
        // The Remainder of args get passed to startup class main(). Make
        // copies of them before we overwrite them with the process name.
        args.add(application ? String8("application") : String8("tool"));
        runtime.setClassNameAndArgs(className, argc - i, argv + i);

        if (!LOG_NDEBUG) {
          String8 restOfArgs;
          char* const* argv_new = argv + i;
          int argc_new = argc - i;
          for (int k = 0; k < argc_new; ++k) {
            restOfArgs.append("\"");
            restOfArgs.append(argv_new[k]);
            restOfArgs.append("\" ");
          }
          ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());
        }
    } else { // ZygoteInit
        // We're in zygote mode.
        maybeCreateDalvikCache();

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

        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]));
        }
    }

这里准备 ZygoteInit 或者 RuntimeInit 启动需要的参数

cpp 复制代码
    if (!niceName.isEmpty()) {
        runtime.setArgv0(niceName.string(), true /* setProcName */);
    }

设置进程名称

cpp 复制代码
    if (zygote) { // 走这里
        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.");
    }
}

启动对应的 Java 类,接下来就会执行到 frameworks/base/core/java/com/android/internal/os/ZygoteInit.javacom.android.internal.os.ZygoteInit 类的 main 函数了。

参考资料

相关推荐
拭心11 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
带电的小王13 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
梦想平凡13 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道14 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
阿甘知识库14 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道15 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频
MuYe15 小时前
Android Hook - 动态加载so库
android
居居飒16 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
Henry_He19 小时前
桌面列表小部件不能点击的问题分析
android
工程师老罗19 小时前
Android笔试面试题AI答之Android基础(1)
android