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

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

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

上一节最后我们分析到调用 runtime.start() 启动 ZygoteInit Java 类:

cpp 复制代码
// AppRuntime 的初始化
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));

// 启动 ZygoteInit 类
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);

这里初始化一个 AppRuntime 对象,然后通过这个对象的 start 方法启动一个 ZygoteInit Java 类。

AppRuntime 直译的话叫做应用运行时,开发时,我们写好 Java/Kotlin 代码,通过对应的编译器将代码编译为字节码,AppRuntime 的作用就是创建一个可以执行字节码的环境,这个环境主要由两部分内容构成:

  • 一部分负责对象的创建与回收,譬如类的加载器,垃圾回收器等
  • 一部分负责程序逻辑的运行,譬如即时编译系统,解释器等

我们先看下 AppRuntime 类的实现:

cpp 复制代码
class AppRuntime : public AndroidRuntime
{
public:
    AppRuntime(char* argBlockStart, const size_t argBlockLength)
        : AndroidRuntime(argBlockStart, argBlockLength)
        , mClass(NULL)
    {
    }

    //......
    // 省略一些代码

    String8 mClassName;
    Vector<String8> mArgs;
    jclass mClass;
};

先看 AppRuntime 的构造函数,调用父类 AndroidRuntime 构造函数:

cpp 复制代码
// AndroidRuntime 类在一个进程中只有一个实例对象,保存在全局变量 gCurRuntime 中。
static AndroidRuntime* gCurRuntime = NULL;

AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :
        mExitWithoutCleanup(false),
        mArgBlockStart(argBlockStart),
        mArgBlockLength(argBlockLength)
{
    // 初始化 skia 图形系统
    SkGraphics::Init();

    // Pre-allocate enough space to hold a fair number of options.
    mOptions.setCapacity(20);

    // 只能被初始化一次
    assert(gCurRuntime == NULL);        // one per process
    // 全局 static 变量
    gCurRuntime = this;
}

构造函数中主要初始化 skia 图形系统,然后把 AndroidRuntime 保存到全局变量 gCurRuntime 中。

初始化完成后,就会调用 start 函数启动虚拟机:

cpp 复制代码
// 启动 ZygoteInit 类
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);

这个函数比较长,我们逐步分析:

cpp 复制代码
//具体实现
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{   

    // 打印启动 log
    ALOGD(">>>>>> START %s uid %d <<<<<<\n",
            className != NULL ? className : "(unknown)", getuid());

当这个 Log 打印时,标志着 Java 的运行时开始启动,如果 Android 的系统 log 中反复出现这段内容,而输出 ID都是Zygote,则说明系统可能出现问题,Zygote进程在不断地重启.

cpp 复制代码
    static const String8 startSystemServer("start-system-server");

    /*
     * 'startSystemServer == true' means runtime is obsolete and not run from
     * init.rc anymore, so we print out the boot start event here.
     */
    for (size_t i = 0; i < options.size(); ++i) {
        if (options[i] == startSystemServer) {
           /* track our progress through the boot sequence */
           const int LOG_BOOT_PROGRESS_START = 3000;
           LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
        }
    }

这里处理参数的异常,对于早期版本的 Runtime,才需要 startSystemServer == true 这样的参数,如果这里出现了这样的参数,打印一些 Log 通知开发人员。

cpp 复制代码
    const char* rootDir = getenv("ANDROID_ROOT");
    if (rootDir == NULL) {
        rootDir = "/system";
        if (!hasDir("/system")) {
            LOG_FATAL("No root directory specified, and /system does not exist.");
            return;
        }
        setenv("ANDROID_ROOT", rootDir, 1);
    }

这里从环境变量 ANDROID_ROOT 中读取到根目录,如果没有读到,则默认设置为 /system,如果 /system 目录不存在,直接退出。

cpp 复制代码
    const char* runtimeRootDir = getenv("ANDROID_RUNTIME_ROOT");
    if (runtimeRootDir == NULL) {
        LOG_FATAL("No runtime directory specified with ANDROID_RUNTIME_ROOT environment variable.");
        return;
    }

    const char* tzdataRootDir = getenv("ANDROID_TZDATA_ROOT");
    if (tzdataRootDir == NULL) {
        LOG_FATAL("No tz data directory specified with ANDROID_TZDATA_ROOT environment variable.");
        return;
    }

这里接着读取环境遍历。

cpp 复制代码
    //const char* kernelHack = getenv("LD_ASSUME_KERNEL");
    //ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);

    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }

这里调用 startVm 启动虚拟机。

cpp 复制代码
    onVmCreated(env);

onVmCreated 是一个虚函数,实际上调用的是子类 AppRuntime 中的重载函数:

cpp 复制代码
    virtual void onVmCreated(JNIEnv* env)
    {
        if (mClassName.isEmpty()) {
            return; // Zygote. Nothing to do here.
        }

    
        char* slashClassName = toSlashClassName(mClassName.string());
        mClass = env->FindClass(slashClassName);
        if (mClass == NULL) {
            ALOGE("ERROR: could not find class '%s'\n", mClassName.string());
        }
        free(slashClassName);

        mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));
    }

如果是 Zygote 进程,变量 mClassName 的值会为 null,会立刻返回。如果是一个普通 Java 类的调用, mClassName 中会存放类的名称,toSlashClassName(mClassName.string()) 的作用是将类名转换为类的全限定名,接着回去到类对应的 jclass 对象。

接着看 start 函数的后续部分:

cpp 复制代码
    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

startReg 的具体实现如下:

cpp 复制代码
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
    ATRACE_NAME("RegisterAndroidNatives");
    /*
     * This hook causes all future threads created in this process to be
     * attached to the JavaVM.  (This needs to go away in favor of JNI
     * Attach calls.)
     */
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);

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

    /*
     * Every "register" function calls one or more things that return
     * a local reference (e.g. FindClass).  Because we haven't really
     * started the VM yet, they're all getting stored in the base frame
     * and never released.  Use Push/Pop to manage the storage.
     */
    env->PushLocalFrame(200);

    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        env->PopLocalFrame(NULL);
        return -1;
    }
    env->PopLocalFrame(NULL);

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

    return 0;
}

// 进一步调用 register_jni_procs
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;
}

static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_com_android_internal_os_RuntimeInit),
    REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
    REG_JNI(register_android_os_SystemClock),
    REG_JNI(register_android_util_EventLog),
    REG_JNI(register_android_util_Log),
    REG_JNI(register_android_util_MemoryIntArray),
    REG_JNI(register_android_util_PathParser),
    REG_JNI(register_android_util_StatsLog),
    REG_JNI(register_android_util_StatsLogInternal),
    REG_JNI(register_android_app_admin_SecurityLog),
    REG_JNI(register_android_content_AssetManager),
    REG_JNI(register_android_content_StringBlock),
    REG_JNI(register_android_content_XmlBlock),
    REG_JNI(register_android_content_res_ApkAssets),
    REG_JNI(register_android_text_AndroidCharacter),
    REG_JNI(register_android_text_Hyphenator),
    REG_JNI(register_android_view_InputDevice),
    REG_JNI(register_android_view_KeyCharacterMap),
    REG_JNI(register_android_os_Process),
    REG_JNI(register_android_os_SystemProperties),
    REG_JNI(register_android_os_Binder),
    REG_JNI(register_android_os_Parcel),
    REG_JNI(register_android_os_HidlSupport),
    REG_JNI(register_android_os_HwBinder),
    REG_JNI(register_android_os_HwBlob),
    REG_JNI(register_android_os_HwParcel),
    REG_JNI(register_android_os_HwRemoteBinder),
    REG_JNI(register_android_os_NativeHandle),
    // ...... 省略大部分
};

startReg 通过调用 register_jni_procs 函数将全局数组 gRegJNI 中的 JNI 本地函数注册到虚拟机中。我们在 Java 层中调用的很多 Native 方法就是在这里注册的。

接着看 start 函数的后续部分:

cpp 复制代码
    /*
     * We want to call main() with a String array with arguments in it.
     * At present we have two arguments, the class name and an option string.
     * Create an array to hold them.
     */
    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;

    stringClass = env->FindClass("java/lang/String");
    assert(stringClass != NULL);
    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
    assert(strArray != NULL);
    classNameStr = env->NewStringUTF(className);
    assert(classNameStr != NULL);
    env->SetObjectArrayElement(strArray, 0, classNameStr);

    for (size_t i = 0; i < options.size(); ++i) {
        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
        assert(optionsStr != NULL);
        env->SetObjectArrayElement(strArray, i + 1, optionsStr);
    }

这里主要是准备执行 Java 类的参数。这些参数保存在 strArray 中。

cpp 复制代码
    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }
    free(slashClassName);

    ALOGD("Shutting down VM\n");
    if (mJavaVM->DetachCurrentThread() != JNI_OK)
        ALOGW("Warning: unable to detach main thread\n");
    if (mJavaVM->DestroyJavaVM() != 0)
        ALOGW("Warning: VM did not shut down cleanly\n");
}

先找到 ZygoteInit 类对应的 jclass, 然后通过 GetStaticMethodID 获得ZygoteInit 类中 main 方法的 ID,接着调用 CallStaticVoidMethod 函数 main 函数。至此,整个 start 函数就分析完了,Zygote进程的初始化过程将转到Java层了。

参考资料

相关推荐
李堇21 分钟前
android滚动列表VerticalRollingTextView
android·java
lxysbly2 小时前
n64模拟器安卓版带金手指2026
android
游戏开发爱好者85 小时前
日常开发与测试的 App 测试方法、查看设备状态、实时日志、应用数据
android·ios·小程序·https·uni-app·iphone·webview
王码码20355 小时前
Flutter for OpenHarmony 实战之基础组件:第三十一篇 Chip 系列组件 — 灵活的标签化交互
android·flutter·交互·harmonyos
黑码哥5 小时前
ViewHolder设计模式深度剖析:iOS开发者掌握Android列表性能优化的实战指南
android·ios·性能优化·跨平台开发·viewholder
亓才孓5 小时前
[JDBC]元数据
android
独行soc5 小时前
2026年渗透测试面试题总结-17(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
金融RPA机器人丨实在智能6 小时前
Android Studio开发App项目进入AI深水区:实在智能Agent引领无代码交互革命
android·人工智能·ai·android studio
科技块儿6 小时前
利用IP查询在智慧城市交通信号系统中的应用探索
android·tcp/ip·智慧城市
独行soc6 小时前
2026年渗透测试面试题总结-18(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮