Android ART VM的启动流程(五)

Android系统的第一个进程是init,init通过解析init.rc脚本来启动关键的守护进程和各种系统服务--其中包括zygote这个应用程序的孵化器。

system/core/rootdir/init.zygote64.rc(这个文件会被import进init.rc中)

service zygote /system/bin/app_process64 -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 audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    writepid /dev/cpuset/foreground/tasks

进程app_process是zygote的载体,app_process启动后,会调用AndroidRuntime的start方法,对应源码:

frameworks/base/cmds/app_process/app_main.cpp

int main(int argc, char* const argv[])
{
    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();
        return 10;
    }
}

AndroidRuntime的start方法负责启动并管理Android虚拟机:

frameworks/base/core/jni/AndroidRuntime.cpp

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    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)));
        }
    }

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

    //const char* kernelHack = getenv("LD_ASSUME_KERNEL");

    /* start the virtual machine */
//step1,初始化jni环境,Init函数确定启动的虚拟机是Dalvik还是ART
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
//step2,启动虚拟机,mJavaVM代表启动后的虚拟机实例
   if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
//step3,虚拟机创建成功,执行回调函数通知调用者,这里的调用者就是app_process程序,所以实际执行的是app_main.cpp中的函数
   onVmCreated(env);

    /*
     * Register android functions.
     */
//step4,注册native函数,都是写预设的jni函数
   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.
     */
//step5,执行目标对象的主函数,也即是zygoteInit的main函数
   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);
        }
    }
    free(slashClassName);
}

上面的函数是启动虚拟机的通用流程,不管虚拟机是Dalvik还是ART,都遵循这个方式,以实现虚拟机的兼容性。

Step1,JniInvocation.init,在虚拟机启动前,需要初始化当前的运行环境,具体由JniInvacation.init函数完成。

libnativehelp/JniInvocation.cpp

bool JniInvocation::Init(const char* library) {
//获取虚拟机动态链接库的地址,默认值给定的是char* kLibraryFallback = "libart.so"
  library = GetLibrary(library, buffer);
  // Load with RTLD_NODELETE in order to ensure that libart.so is not unmapped when it is closed.
  // This is due to the fact that it is possible that some threads might have yet to finish
  // exiting even after JNI_DeleteJavaVM returns, which can lead to segfaults if the library is
  // unloaded.
//打开虚拟机动态链接库
 const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE;
  handle_ = dlopen(library, kDlopenFlags);
  if (handle_ == NULL) {
    if (strcmp(library, kLibraryFallback) == 0) {
      // Nothing else to try.
      ALOGE("Failed to dlopen %s: %s", library, dlerror());
      return false;
    }
    // Note that this is enough to get something like the zygote
    // running, we can't property_set here to fix this for the future
    // because we are root and not the system user. See
    // RuntimeInit.commonInit for where we fix up the property to
    // avoid future fallbacks. http://b/11463182
    ALOGW("Falling back from %s to %s after dlopen error: %s",
          library, kLibraryFallback, dlerror());
    library = kLibraryFallback;
    handle_ = dlopen(library, kDlopenFlags);
    if (handle_ == NULL) {
      ALOGE("Failed to dlopen %s: %s", library, dlerror());
      return false;
    }
  }
//查找虚拟机库中3个重要的接口实现
 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;
}

获取到library库的名称(libart.so)后,通过dlopen把他打开并加载到内存,C语言为处理动态链接库提供了一些函数,如dlopen,dlsym,dladdr等,对应的实现在bionic/linker中,dlopen是以指定的模式打开动态链接库,RTLD_NOW表示在dlopen时直接解析所有未定义的符号,RTLD_NODELETE表示dlclose时不卸载动态库,那么后续如果该动态库重新被加载的话,其中的静态变量就不需要重新初始化了。

然后查找3个关键的接口实现,JNI_GetDefaultJavaVMInitArgs,JNI_CreateJavaVM,JNI_GetCreatedJavaVMs,分别保存在相应的变量中(JNI_GetDefaultJavaVMInitArgs_,JNI_CreateJavaVM_,JNI_GetCreatedJavaVMs_),这三个函数可以认为是Android虚拟机必须实现的通用接口,不管是art还是dalvik都要实现这3个接口,这样就能保证Android系统正确的使用他们。


Step2,StartVM,主要完成的工作有:

1)初始化虚拟机参数,ART跟Dalvik,JVM类似,支持一系列的配置参数,这些参数的默认值通常来自于系统属性,相关代码:

 parseRuntimeOption("dalvik.vm.heapsize", heapsizeOptsBuf, "-Xmx", "16m");

parseCompilerRuntimeOption("dalvik.vm.dex2oat-Xms", dex2oatXmsFlagsBuf, "-Xms", "-Xcompiler-option");

2)StartVM的最后,调用JNI_CreateJavaVM,前面设置的启动参数会保存在变量nOptions中,然后通过JNI_CreateJavaVM这一标准接口传递给具体的虚拟机实现。JNI_CreateJavaVM成功返回,虚拟机就处于就绪状态了,可以接受jni调用了。

虚拟机分为java和native两个层面,这两层间的通信就是jni,通常虚拟机的创建和管理都是在native层,然后通过jni来与java层互访,JNI_CreateJavaVM成功执行后,会有一个JNIEnv类型的指针变量,就是pEnv,他是以指向指针的指针做为参数的。这个JNIEnv指针是访问JVM的关键,真正的实现是JNIEnvExt。

art/runtime/jni_env_ext.h ,从这里可以看出实现关系

struct JNIEnvExt : public JNIEnv {
  static JNIEnvExt* Create(Thread* self, JavaVMExt* vm);

  ~JNIEnvExt();
}

Step3,onVmCreated,虚拟机成功创建完成,需要通过回调函数onVmCreate()通知用户,这里的调用者就是AndroidRuntime的继承类AppRuntime,


Step4,startReg,为虚拟机注册本地函数,

frameworks/base/core/jni/AndroidRuntime.cpp

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.)
     */
   //这个调用会让后面创建的新线程都会hooked到JavaVM上。
    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.
     */
//PushLocalFrame,PopLocalFrame的作用是管理局部引用的生命周期,要配套使用。
    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的作用是注册gRegJNI数组中的所有的nativeMethod:

static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_android_util_SeempLog),
    REG_JNI(register_com_android_internal_os_RuntimeInit),
    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_app_admin_SecurityLog),
......

注册的过程是分别调用gRegJNI数组中每个元素,展开REG_JNI宏定义,实际执行的就是REG_JNI()括号中的函数,通过AndroidRuntime,这个函数最终会调用Java虚拟机中的RegisterNativeMethods方法,注册的过程就是把Java层的函数跟本地层的实现建立关联,这些java层函数和native函数的映射关系以JNINativeMethod gMethods[]数组来定义的,

typedef struct {
    const char* name;
    const char* signature;
    void*       fnPtr;
} JNINativeMethod;

宏定义REG_JNI中的每个函数都有一个这样的数组。从JNI的实现看,JVM所做的工作就是把这写本地函数设置为ArtMethod的JNI入口。

step5,AndroidRuntime::start的第五步,调用zygoteInit.java的main函数,正式启动一个Java程序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值