AOSP 8.0 系统启动之四ART虚拟机启动(二)

前言

上一章节已经梳理了通过加载libart.so,  JNI_CreateJavaVM成功返回,虚拟机就处于就绪状态了,可以接受jni调用了。

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

art/runtime/jni_env_ext.h

#include <jni.h>   ===> libnativehelper/include/include_jni/jni.h

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

涉及源码

​platform/frameworks/base/core/jni/AndroidRuntime.cpp
/frameworks/base/core/jni/android_util_Binder.cpp
/frameworks/base/core/jni/core_jni_helpers.h
/libnativehelper/JNIHelp.cpp
/art/runtime/jni_internal.cc

一、虚拟机启动回调-onVmCreated

注册jni之前,虚拟机已经启动成功后,一个回调onVmCreated

onVmCreated,虚拟机成功创建完成,需要通过回调函数onVmCreate()通知用户,这里的调用者就是AndroidRuntime的继承类AppRuntime来完成,如果是zygote启动的,则不做任何逻辑,直接return。

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{

    ... //打印一些日志,获取ANDROID_ROOT环境变量
    
    onVmCreated(env);//表示虚拟创建完成,但是里面是空实现

    
    if (startReg(env) < 0) {注册JNI函数
        ALOGE("Unable to register all android natives\n");
        return;
    }
    
    ... //JNI方式调用ZygoteInit类的main函数
}

二、注册JNI

2.1 startReg

定义在platform/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.)
     */
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
    //设置Android创建线程的函数javaCreateThreadEtc,这个函数内部是通过Linux的clone来创建线程的,这个调用会让后面创建的新线程都会hooked到JavaVM上。后续线程专项会重点介绍

    ....
    env->PushLocalFrame(200);//创建一个200容量的局部引用作用域,这个局部引用其实就是局部变量

    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) { //注册JNI函数
        env->PopLocalFrame(NULL);
        return -1;
    }
    env->PopLocalFrame(NULL);//释放局部引用作用域


    return 0;
}

startReg首先是设置了Android创建线程的处理函数,然后创建了一个200容量的局部引用作用域,用于确保不会出现OutOfMemoryException,
最后就是调用register_jni_procs进行JNI注册

2.1.1  register_jni_procs

定义在platform/frameworks/base/core/jni/AndroidRuntime.cpp

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) { //调用mProc, 是一个函数指针
            return -1;
        }
    }
    return 0;
}

struct RegJNIRec {
   int (*mProc)(JNIEnv*);
};

它的处理是交给RegJNIRec的mProc,RegJNIRec是个很简单的结构体,mProc是个函数指针

我们看看register_jni_procs传入的RegJNIRec数组gRegJNI,里面就是一堆的函数指针

extern int register_android_os_Binder(JNIEnv* env);
extern int register_android_os_Process(JNIEnv* env);


static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_com_android_internal_os_RuntimeInit),
    REG_JNI(register_com_android_internal_os_ZygoteInit),//重点拿这个Zygote的JNI 注册来分析
    ......
    REG_JNI(register_android_os_Binder),  //重点拿这个binder的JNI 注册来分析
    .....
}

REG_JNI是一个宏定义

    #define REG_JNI(name)      { name }
    struct RegJNIRec {
        int (*mProc)(JNIEnv*);
    };

也就是说REG_JNI(register_com_android_internal_os_ZygoteInit)这句就相当于,{register_com_android_internal_os_ZygoteInit},
也就是将register_com_android_internal_os_ZygoteInit强转为 int (mProc)(JNIEnv) 这样一个方法指针,于是就可以array[i].mProc(env)这样调用,
等同于调用register_com_android_internal_os_ZygoteInit(JNIEnv* env)这个方法

再看看register_com_android_internal_os_ZygoteInit,这实际上是自定义JNI函数并进行动态注册的标准写法,
内部是调用JNI的RegisterNatives,这样注册后,Java类ZygoteInit的native方法nativeZygoteInit就会调用com_android_internal_os_ZygoteInit_nativeZygoteInit函数

int register_com_android_internal_os_ZygoteInit(JNIEnv* env)
{
    const JNINativeMethod methods[] = {
        { "nativeZygoteInit", "()V",
            (void*) com_android_internal_os_ZygoteInit_nativeZygoteInit },
    };
    return jniRegisterNativeMethods(env, "com/android/internal/os/ZygoteInit",
        methods, NELEM(methods));
}

/frameworks/base/core/jni/android_util_Binder.cpp

/frameworks/base/core/jni/android_util_Binder.cpp
int register_android_os_Binder(JNIEnv* env)
{
    //注册Binder
    if (int_register_android_os_Binder(env) < 0)
        return -1;
    //注册BinderInternal
    if (int_register_android_os_BinderInternal(env) < 0)
        return -1;
    //注册BinderProxy
    if (int_register_android_os_BinderProxy(env) < 0)
        return -1;

    jclass clazz = FindClassOrDie(env, "android/util/Log");
    gLogOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gLogOffsets.mLogE = GetStaticMethodIDOrDie(env, clazz, "e",
            "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I");

    clazz = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
    gParcelFileDescriptorOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gParcelFileDescriptorOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "<init>",
                                                                 "(Ljava/io/FileDescriptor;)V");

    clazz = FindClassOrDie(env, "android/os/StrictMode");
    gStrictModeCallbackOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gStrictModeCallbackOffsets.mCallback = GetStaticMethodIDOrDie(env, clazz,
            "onBinderStrictModePolicyChange", "(I)V");

    return 0;
}

2.1.2  int_register_android_os_Binder

const char* const kBinderPathName = "android/os/Binder";

static int int_register_android_os_Binder(JNIEnv* env)
{
    jclass clazz = FindClassOrDie(env, kBinderPathName);  //通过classlinker加载jclass

    gBinderOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
    gBinderOffsets.mExecTransact = GetMethodIDOrDie(env, clazz, "execTransact", "(IJJI)Z");
    gBinderOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");

    return RegisterMethodsOrDie( env, kBinderPathName, gBinderMethods, NELEM(gBinderMethods));
}

2.1.3 RegisterMethodsOrDie

/frameworks/base/core/jni/core_jni_helpers.h
static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className,
                                       const JNINativeMethod* gMethods, int numMethods) {
    int res = AndroidRuntime::registerNativeMethods(env, className, gMethods, numMethods);
    return res;
}

2.1.4 AndroidRuntime::registerNativeMethods

/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
    const char* className, const JNINativeMethod* gMethods, int numMethods)
{
    return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}


/libnativehelper/JNIHelp.cpp
extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
    const JNINativeMethod* gMethods, int numMethods)
{
    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);

    ALOGV("Registering %s's %d native methods...", className, numMethods);

    scoped_local_ref<jclass> c(env, findClass(env, className));
    
    if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
        char* tmp;
        const char* msg;
         if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) {
             // Allocation failed, print default warning.
             msg = "RegisterNatives failed; aborting...";
         } else {
             msg = tmp;
         }
         e->FatalError(msg);
     }
 
     return 0;
}

2.1.5 RegisterNatives

/art/runtime/jni_internal.cc

static jint RegisterNatives(JNIEnv* env, jclass java_class, const JNINativeMethod* methods,
                            jint method_count) {
	return RegisterNativeMethods(env, java_class, methods, method_count, true);
}

static jint RegisterNativeMethods(JNIEnv* env, jclass java_class, const JNINativeMethod* methods,
                                  jint method_count, bool return_errors) {
  ScopedObjectAccess soa(env);
  StackHandleScope<1> hs(soa.Self());
  Handle<mirror::Class> c = hs.NewHandle(soa.Decode<mirror::Class>(java_class));
  
  CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", methods, JNI_ERR);
  for (jint i = 0; i < method_count; ++i) {
    const char* name = methods[i].name;
    const char* sig = methods[i].signature;
    const void* fnPtr = methods[i].fnPtr;
    
    bool is_fast = false;
    

    // Note: the right order is to try to find the method locally
    // first, either as a direct or a virtual method. Then move to
    // the parent.
    ArtMethod* m = nullptr;
    // 根据java_class找到匹配的methods里面的名字,找到匹配的java类,可能native在父类
    for (ObjPtr<mirror::Class> current_class = c.Get();
         current_class != nullptr;
         current_class = current_class->GetSuperClass()) {
      // Search first only comparing methods which are native.
      // 找到匹配的ArtMethod m
      m = FindMethod<true>(current_class.Ptr(), name, sig);
      if (m != nullptr) {
        break;
      }
      .....
    }
    .....

    const void* final_function_ptr = m->RegisterNative(fnPtr, is_fast);
    UNUSED(final_function_ptr);
  }
  return JNI_OK;
}

2.1.6  ArtMethod::RegisterNative

const void* ArtMethod::RegisterNative(const void* native_method, bool is_fast) {
  CHECK(IsNative()) << PrettyMethod();
  CHECK(!IsFastNative()) << PrettyMethod();
  CHECK(native_method != nullptr) << PrettyMethod();
  if (is_fast) {
    AddAccessFlags(kAccFastNative);
  }
  void* new_native_method = nullptr;
  Runtime::Current()->GetRuntimeCallbacks()->RegisterNativeMethod(this,
                                                                  native_method,
                                                                  /*out*/&new_native_method);
  SetEntryPointFromJni(new_native_method);
  return new_native_method;
}

最后有RegisterNative完成注册工作,它要做的就是将native method在内存中地址通过SetEntryPointFromJni设置为对应ArtMethod的JNI入口。在JNI环境下,当java层的函数被调用后,首先找到它的ArtMethod对象,然后通过GetEntryPoint得到JNI入口代码的地址。

这就完成了java层跟native层函数的JNI映射。

2.1.7 Fast Native

这块简单介绍一下 fast jni模式

下文参考资料(《深入理解ART虚拟机》)

安卓 函数执行 分为两条线 第一种是 Java层,第二种JNI层 也就是 so层
当函数调用Java层进入到JNI层的是时候,虚拟机会将执行线程的状态从Runnable转换为Native。
如果JNI层又调用Java层相关函数的时候,执行线程的状态又得从Native层转换为Runnable。

线程的切换需要浪费时间,所以,对于某个特别强调执行速度的JNI函数可以设置成 fast jni模式


这种模式下执行这个native函数 将不会进行 状态切换,即执行线程的状态 始终为Runnable。


当然,这种模式的使用对GC有一些影响,所哟最好在那些本身函数执行时间段的,又不会阻塞的情况下使用。
另外,这种模式目前在art虚拟机内部 很多java native都有使用

为了和其他Native函数 进行区分,当使用fast jni模式的函数的签名信息 必须以 “!”开头

参考 使用 @FastNative 和 @CriticalNative 的区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值