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层了。

参考资料

相关推荐
柿蒂1 小时前
聊聊SliverPersistentHeader优先消费滑动的设计
android·flutter
假装多好1233 小时前
android三方调试几个常用命令
android·1024程序员节·三方,gms
侧耳4293 小时前
android11禁止安装apk
android·java·1024程序员节
JohnnyDeng944 小时前
ArkTs-Android 与 ArkTS (HarmonyOS) 存储目录全面对比
android·harmonyos·arkts·1024程序员节
2501_915918414 小时前
iOS 26 查看电池容量与健康状态 多工具组合的工程实践
android·ios·小程序·https·uni-app·iphone·webview
limingade4 小时前
手机摄像头如何识别体检的色盲检查图的数字和图案(下)
android·1024程序员节·色盲检查图·手机摄像头识别色盲图案·android识别色盲检测卡·色盲色弱检测卡
文火冰糖的硅基工坊5 小时前
[嵌入式系统-150]:智能机器人(具身智能)内部的嵌入式系统以及各自的功能、硬件架构、操作系统、软件架构
android·linux·算法·ubuntu·机器人·硬件架构
2501_915909066 小时前
iOS 架构设计全解析 从MVC到MVVM与使用 开心上架 跨平台发布 免Mac
android·ios·小程序·https·uni-app·iphone·webview
明道源码6 小时前
Android Studio 创建 Android 模拟器
android·ide·android studio
明道源码7 小时前
Android Studio 使用教程
android·ide·android studio