art虚拟机启动过程分析

本文详细分析了Android ART虚拟机从app_process启动到Zygote进程创建的每一步,包括AppRuntime、JniInvocation、Runtime和Thread等关键组件的初始化。通过时序图和类图,展示了从main函数到JNIEnvExt、JavaVMExt的创建过程,以及如何通过JNI调用创建JavaVM。最终,调用ZygoteInit的main函数,开启Java世界。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

标签(空格分隔): art android5.1 启动流程 jvm


我们都已经知道,Android系统是基于Linux内核,而应用程序大都由Java语言实现的一个操作系统,包含一套C/C++ Framework和Java Framework以及其他的库支持文件和Hal层的一些东西。Android的启动过程也是从/system/core/init/init.c的main函数开始(这里忽略了bootloader的引导以及Linux Kernel的启动)。init.c这个文件做的事情比较多,这里我们只关注和Java层有关的东西。init.c中解析init.rc文件,在init.rc中和Java世界相关的是zygote,它在一个脚本文件中被系统创建出来,如下

    service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

在Linux系统下,app_process是一个bin类型的可执行文件,它的源码在/frameworks/base/cmds/app_process中,其程序入口为main函数。在分析之前,我们先将这个过程的时序图和涉及到的一些类信息简单看看,有个宏观的感知。

时序图如下:

相关类图:

JavaVM:进程相关,每个虚拟机进程有持有一个JavaVM对象
JNIEnv:线程相关,每个虚拟机线程有持有一个JNIEnv对象
Runtime:Java程序运行时环境
Thread:Java Thread对象

Step1: app_process.main (/frameworks/base/cmds/app_process)

由命令行./system/bin/app_process -Xzygote /system/bin –zygote –start-system-server调用。

int main(int argc, char* const argv[])
{
    ....
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    argc--;
    argv++;
    int i;
    for (i = 0; i < argc; i++) {
        if (argv[i][0] != '-') {
            break;
        }
        if (argv[i][1] == '-' && argv[i][2] == 0) {
            ++i; // Skip --.
            break;
        }
        runtime.addOption(strdup(argv[i]));
    }
    ....
    // Parse runtime arguments.  Stop at first unrecognized option.
    ++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;
        }
    }
    ....
    if (!className.isEmpty()) {
        ....
    } else {
        // We're in zygote mode.
        maybeCreateDalvikCache();
        ...
    }
    if (!niceName.isEmpty()) {
        runtime.setArgv0(niceName.string());
        set_process_name(niceName.string());
    }
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args);
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
        return 10;
    }
}

代码先是创建了一个AppRuntime对象,它继承自AndroidRuntime,构造函数参数分别是命令行字符串的首地址和字符串的总长度,然后将这两个值存放到其父类的成员变量mArgBlockStart和mArgBlockLength中,这里分析下AndroidRuntime成员变量mOptions,相关定义为

    Vector<JavaVMOption> mOptions;
    typedef struct JavaVMOption {
        const char* optionString;
        void*       extraInfo;
    } JavaVMOption;

可知mOptions就是用来保存这些JavaVMOption参数的,其成员函数addOption负责往mOptions中添加元素,main函数中首先将命令行中第一个以”-“开头的字符串加入到mOptions中,这里即是”-Xzygote”,继续解析命令行参数,while循环完成后,此时zygote和startSystemServer为true niceName为“zygote”,表示当前是在zygote模式下,接下来调用maybeCreateDalvikCache方法,创建这个/data/dalvik-cache/x86文件(我这里是x86的架构,还有arm等),存放字节码的缓存文件。接下来设置abi的相关信息,args最终存放的就是[“start-system-server”, “–abi-list=x86”]。此时niceName非空,接下来调用runtime.setArgv0,同时设置当前进程的name为niceName。

    void AndroidRuntime::setArgv0(const char* argv0) {
        memset(mArgBlockStart, 0, mArgBlockLength);
        strlcpy(mArgBlockStart, argv0, mArgBlockLength);
    }

这个函数调用memeset清除这块内存区,同时将niceName copy放置到mArgBlockStart的地址空间处。最后由于zygote为true,会进入runtime.start函数,此时args参数为”start-system-server”, “–abi-list=x86”。

Step2: AppRuntime::start (/frameworks/base/core/jni/AndroidRuntime.cpp)

AppRuntime继承至AndroidRuntime,其start方法实现如下:

void AndroidRuntime::start(const char* className, const Vector<String8>& options)
{
    ....
    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env) != 0) {
        return;
    }
    onVmCreated(env);

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

    /*
     * 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);
    }

    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    char* slashClassName = toSlashClassName(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");
}

这个方法控制了整个启动的流程,主要由以下几步:
->创建JniInvocation,加载art虚拟机so文件(libart.so)。
->调用函数startVM,创建JavaVM和JNIEnv
->注册Android jni方法
->启动到Java世界
下面分段阅读这几个过程。

Step3: JniInvocation::Init (/libnativehelper/JniInvocation.cpp)

该函数中首先获取要加载的library名字,默认是libart.so库文件,并打开这个动态库并解析出所有未定义符号,同时返回动态链接库的句柄给handle_指针变量。接下来继续解析,将JNI_GetDefaultJavaVMInitArgs_、JNI_CreateJavaVM_、JNI_GetCreatedJavaVMs_三个函数指针进行初始化。生成libart.so的源代码位于/art/runtime,其中在jni_internal.cc中定义有这三个函数。

bool JniInvocation::Init(const char* library) {
  ....
  library = GetLibrary(library, buffer);
  handle_ = dlopen(library, RTLD_NOW);
  ....
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),
                  "JNI_GetDefaultJavaVMInitArgs")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
                  "JNI_CreateJavaVM")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),
                  "JNI_GetCreatedJavaVMs")) {
    return false;
  }
  return true;
}

函数执行完毕之后,返回至AndroidRuntime的start,将控制权归还,接着执行start中的startVM函数。

Step4: AndroidRuntime::startVm (/frameworks/base/core/jni/AndroidRuntime.cpp)

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
    int result = -1;
    JavaVMInitArgs initArgs;
    ....
    initArgs.version = JNI_VERSION_1_4;
    initArgs.options = mOptions.editArray();
    initArgs.nOptions = mOptions.size();
    initArgs.ignoreUnrecognized = JNI_FALSE;

    /*
     * Initialize the VM.
     *
     * The JavaVM* is essentially per-process, and the JNIEnv* is per-thread.
     * If this call succeeds, the VM is ready, and we can start issuing
     * JNI calls.
     */
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
        ALOGE("JNI_CreateJavaVM failed\n");
        goto bail;
    }
    result = 0;
bail:
    return result;
}

这个函数中,通过property_get获取系统属性文件,设置大量关于虚拟机的一些参数信息,addOption、parseExtraOpts、parseRuntimeOption、parseCompilerRuntimeOption这四个函数均是往mOptions这个容器中添加元素,经过一些列的参数设置,在6-9行将mOptions信息保存至结构体initArgs中,initArgs是一个JavaVMInitArgs结构体对象

typedef struct JavaVMInitArgs {
    jint        version;    /* use JNI_VERSION_1_2 or later */

    jint        nOptions;
    JavaVMOption* options;
    jboolean    ignoreUnrecognized;
} JavaVMInitArgs;

继续往下,接着调用JNI_CreateJavaVM去进一步创建JavaVM对象。

Step5: JniInvocation.JNI_CreateJavaVM (/libnativehelper/JniInvocation.cpp)

extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) {
  return JniInvocation::GetJniInvocation().JNI_GetCreatedJavaVMs(vms, size, vm_count);
}

jint JniInvocation::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值