美文网首页
JNI的源码实现分析

JNI的源码实现分析

作者: 拉丁吴 | 来源:发表于2024-04-01 13:54 被阅读0次

    在之前的文章中,我们熟悉了JNI的代码实现,并尝试手写了一个JNI的demo,实现了Java与cpp层的沟通。但是如果仔细思考还是会有一些困惑,Java和cpp最开始究竟是如何找到对方的?那种方法的映射在系统层面是如何建立起来的。这些问题是JNI规范文档所不能告诉我们的,需要我们自己找到答案。而答案就在源码里。

    JNI的背景知识补充

    JNI是Java语言定义的与cpp进行沟通的接口,接口具体由不同的虚拟来实现。关于JNI有以下几个比较重要的类型:

    • JavaVM:表示Java虚拟机,进程相关
    • JNIEnv:表示JNI环境的上下文,例如注册、查找类、异常等。 线程相关
    • jclass:在JNI中表示对应的的Java类。
    • jmethodID:在JNI中表示对应的的Java类中的方法的id。
    • jfiledID:在JNI中表示对应的Java类中的属性的id。
    • thread:JNI中通过AttachCurrentThread和DetachCurrentThread方法,实现和Java线程的结合。

    在Android中,JNI的接口声明在系统的libnativehelper.so的动态库中,相关源码在libnativehelper/include_jni/jni.h。而JNI的实现则在虚拟机中,比如JNI中的函数操作表的实现就在art的art/runtime/jni/jni_internal.cc中。

    cpp与Java的初次联系

    我们假设一个时间点,在这个时间点之前,Java与cpp没有任何联系,而在这个时间点之后,他们都知道了对方的存在,建立起了沟通机制。

    这个时间点cpp代码会第一次创建JVM虚拟机,创建JNIEnv上下文环境,同时导入JNI的操作函数表,并调用Java层的一个入口方法。这个时间点就是Android开机启动过程中的zygote进程的启动。

    从zygote看起

    我们就从zygote进程的入口函数开始看起。

    
    // frameworks/base/cmds/app_process/app_main.cpp
    
    int main(int argc, char* const argv[])
    {
        ...
        ...
        // 初始化AppRuntime AppRuntime继承了AndroidRuntime
        AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
        ...
        ...
        // Parse runtime arguments.  Stop at first unrecognized option.
        bool zygote = false;
        bool startSystemServer = false;
        bool application = false;
        String8 niceName;
        String8 className;
    
        ++i;  // Skip unused "parent dir" argument.
        while (i < argc) {
            const char* arg = argv[i++];
            // zygote进程
            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 (!niceName.isEmpty()) {
            runtime.setArgv0(niceName.string(), true /* setProcName */);
        }
    
        if (zygote) { // zygote进程,传入的com.android.internal.os.ZygoteInit是Java类
            runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
        } else if (!className.isEmpty()) {
            runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
        } else {
            fprintf(stderr, "Error: no class name or --zygote supplied.\n");
            app_usage();
            LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
        }
    }
    
    

    这是zygote进程的初始化程序,我们看到的AppRuntime继承自AndroidRuntime:

    image.png

    看一下AndroidRuntime的start方法

    void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
    {
    
        // 进入start方法主要办了三件事
       ...
       ...
       // 1 加载本地库,创建虚拟机,导入JNI函数表
       // 初始化JNI的函数操作,
        JniInvocation jni_invocation;
        // 就是加载系统默认的so库并从中导入相关的接口
        jni_invocation.Init(NULL);
        JNIEnv* env; // 定义一个JNIEnv指针
        // 
        if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
            return;
        }
        // 虚拟机创建后的回调方法
        onVmCreated(env);
    
    // 2,通过JNI 动态注册Android中已有的Java native 函数
        // 开始注册Android已有的Java native方法
        if (startReg(env) < 0) {
            ALOGE("Unable to register all android natives\n");
            return;
        }
    
     // 3 调用com.android.internal.os.ZygoteInit.main() 这个静态方法入口,从cpp进入Java层
        char* slashClassName = toSlashClassName(className != NULL ? className : "");
        // 找到java classname:com.android.internal.os.ZygoteInit
        jclass startClass = env->FindClass(slashClassName);
        if (startClass == NULL) {
            ...
        } else {
            // 找到com.android.internal.os.ZygoteInit里面的main方法
            jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
                "([Ljava/lang/String;)V");
            if (startMeth == NULL) {
                ...
            } else {
                //调用mian方法入口,进入Java层
                env->CallStaticVoidMethod(startClass, startMeth, strArray);
            }
        }
       
    }
    
    
    
    

    如代码注释所述,start方法主要做了三件事:

      • 创建虚拟机,配置JNI函数
      • 使用JNI动态注册Android系统已有的native方法
      • 调用com.android.internal.os.ZygoteInit的main入口,进入Java层

    创建虚拟机

    /***********  frameworks/base/core/jni/AndroidRuntime.cpp ********************/
    
    int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote)
    {
       
       JavaVMInitArgs initArgs; 
        ...
        ...
        //一些虚拟机的配置
        ...
    
        initArgs.version = JNI_VERSION_1_4;
        initArgs.options = mOptions.editArray();
        initArgs.nOptions = mOptions.size();
        initArgs.ignoreUnrecognized = JNI_FALSE;
    
        // JNI_CreateJavaVM 创建Java虚拟机
        if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
            ALOGE("JNI_CreateJavaVM failed\n");
            return -1;
        }
    
        return 0;
    }
    
    
    /***********  art/runtime/jni/java_vm_ext.cc ********************/
    
    // art虚拟机实现
    extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
    
      const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
      // 判断下版本号,只能是是JNI_VERSION_1_2 JNI_VERSION_1_4 JNI_VERSION_1_6,不能瞎传
      if (JavaVMExt::IsBadJniVersion(args->version)) {
        LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
        return JNI_EVERSION;
      }
    
      bool ignore_unrecognized = args->ignoreUnrecognized;
      // 创建 Runtime  具体逻辑见下面的函数 Runtime::Create
      if (!Runtime::Create(options, ignore_unrecognized)) {
        return JNI_ERR;
      }
    
      //把Android系统的默认库都加载进内存中
      android::InitializeNativeLoader();
      // 获取刚才创建的Runtime对象
      Runtime* runtime = Runtime::Current();
      // 调用start方法,注册runtime相关的jni方法,以及设置一些基础环境
      // 具体逻辑见下面的Runtime::Start函数
      bool started = runtime->Start();
      if (!started) {
        delete Thread::Current()->GetJniEnv();
        delete runtime->GetJavaVM();
        LOG(WARNING) << "CreateJavaVM failed";
        return JNI_ERR;
      }
       // p_env指针指向 当前线程中获取JNIEnv
      *p_env = Thread::Current()->GetJniEnv();
      // p_vm指针指向 Runtime中获取的JavaVM
      // 此时Javavm已经创建成功
      *p_vm = runtime->GetJavaVM();
      return JNI_OK;
    }
    
    
    
    /***********  art/runtime/runtime.cc ********************/
    
    /************************创建 Runtime  start *********************************/
    bool Runtime::Create(const RuntimeOptions& raw_options, bool ignore_unrecognized) {
      RuntimeArgumentMap runtime_options;
      //  调用Create()函数
      return ParseOptions(raw_options, ignore_unrecognized, &runtime_options) &&
          Create(std::move(runtime_options));
    }
    // 创建Runtime
    bool Runtime::Create(RuntimeArgumentMap&& runtime_options) {
      if (Runtime::instance_ != nullptr) {
        return false;
      }
      // Runtime类很大,就不列举了,有兴趣可以去看头文件art/runtime/runtime.h
      instance_ = new Runtime; // 手动申请内存空间,创建Runtime对象
      Locks::SetClientCallback(IsSafeToCallAbort);
      // 在init逻辑里创建JavaVM的对象
      if (!instance_->Init(std::move(runtime_options))) {
        ...
        return false;
      }
      return true;
    }
    // 创建Java虚拟机
    void Runtime::Init(){
    
        ...
        ...
     java_vm_ = JavaVMExt::Create(this, runtime_options, &error_msg);
      if (java_vm_.get() == nullptr) {
        LOG(ERROR) << "Could not initialize JavaVMExt: " << error_msg;
        return false;
      }
    
      // 把一个获取与当前线程绑定的JNIEnv对象 的钩子函数 保存起来,
      // 便于以后直接通过java_vm->GetEnv()接口来获取对应的JNIEnv
      java_vm_->AddEnvironmentHook(JNIEnvExt::GetEnvHandler);
      
      ...
      ...
    
    }
    
    // GetEnvHandler 钩子函数
    jint JNIEnvExt::GetEnvHandler(JavaVMExt* vm, /*out*/void** env, jint version) {
    
      if (JavaVMExt::IsBadJniVersion(version) && version != JNI_VERSION_1_1) {
        return JNI_EVERSION;
      }
      Thread* thread = Thread::Current();
      *env = thread->GetJniEnv();
      return JNI_OK;
    }
    
    /************************创建 Runtime  end *********************************/
    
    
    /************************创建 Runtime::strat  start *********************************/
    // runtime::start方法
    // 主要注册
    bool Runtime::Start() {
    
    ...
    ...
      // 获取当前线程
      Thread* self = Thread::Current();
      started_ = true;
    
      // JNI动态注册Runtime相关的native方法
      // 具体函数逻辑见下面的RegisterRuntimeNativeMethods函数
      RegisterRuntimeNativeMethods(self->GetJniEnv());
    
    // 创建jit
        CreateJit();
    
    // 创建SystemClassLoader
      system_class_loader_ = CreateSystemClassLoader(this);
    
    ...
    ...
      return true;
    }
    
    // 执行Runtime相关的类的JNI注册
    void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
    ...
      register_dalvik_system_VMRuntime(env);
      register_java_lang_String(env); // 下面以String为例,展开具体函数逻辑
    ...
    
    }
    
    
    // string的native方法名,方法签名等信息
    static JNINativeMethod gMethods[] = {
      FAST_NATIVE_METHOD(String, charAt, "(I)C"),
      FAST_NATIVE_METHOD(String, compareTo, "(Ljava/lang/String;)I"),
      FAST_NATIVE_METHOD(String, concat, "(Ljava/lang/String;)Ljava/lang/String;"),
      FAST_NATIVE_METHOD(String, doRepeat, "(I)Ljava/lang/String;"),
      FAST_NATIVE_METHOD(String, doReplace, "(CC)Ljava/lang/String;"),
      FAST_NATIVE_METHOD(String, fastSubstring, "(II)Ljava/lang/String;"),
      FAST_NATIVE_METHOD(String, getCharsNoCheck, "(II[CI)V"),
      FAST_NATIVE_METHOD(String, fillBytesLatin1, "([BI)V"),
      FAST_NATIVE_METHOD(String, fillBytesUTF16, "([BI)V"),
      FAST_NATIVE_METHOD(String, intern, "()Ljava/lang/String;"),
      FAST_NATIVE_METHOD(String, toCharArray, "()[C"),
    };
    void register_java_lang_String(JNIEnv* env) {
      REGISTER_NATIVE_METHODS("java/lang/String");
      //该宏定义 最终调用了env->RegisterNatives(c.get(), methods, method_count);完成了动态注册
    }
    
    /************************创建 Runtime::strat  end *********************************/
    
    

    虚拟机创建成功之后,就为Java代码的运行提供了基础环境,为从cpp进入Java世界做好了准备

    跟了上面的代码,我们了解到创建虚拟机的逻辑中就已经包含了一些JNI方法的动态注册了,不过主要是是虚拟机相关的。

    动态注册系统JNI方法

    紧接着第二步,注册Android系统原生的方法startReg(env)

    // REG_JNI宏定义
    #define REG_JNI(name)      { name }
    // 定义一个RegJNIRec,里面是一个函数类型的指针,函数的参数是JNIEnv指针
    struct RegJNIRec {
        int (*mProc)(JNIEnv*);
    };
    
    
    
     int AndroidRuntime::startReg(JNIEnv* env)
    {
        ...
        ...
        
        if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
            env->PopLocalFrame(NULL);
            return -1;
        }
        env->PopLocalFrame(NULL);
    
    
        return 0;
    }
    
    
    
    // RegJNIRec数组,里面代表了需要注册的Java native方法
    static const RegJNIRec gRegJNI[] = {
    // register_com_android_internal_os_RuntimeInit 本身就是一个函数
            REG_JNI(register_com_android_internal_os_RuntimeInit),
            REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
            ...
            ...
    };
    
    
    // 注册函数
    static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
    {
        for (size_t i = 0; i < count; i++) {
            // 遍历gRegJNI数组的元素,并调用元素的方法mProc
            // 比如array[0].mProc(env) 就是调用 register_com_android_internal_os_RuntimeInit(env)
            if (array[i].mProc(env) < 0) {
                return -1;
            }
        }
        return 0;
    }
    // 动态注册RuntimeInit本地方法
    int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
    {
    // 可以看到主要是为两个native方法nativeFinishInit,nativeSetExitWithoutCleanup进行注册
    // 他们映射的cpp函数分别是 com_android_internal_os_RuntimeInit_nativeFinishInit  
    // com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup
        const JNINativeMethod methods[] = {
                {"nativeFinishInit", "()V",
                 (void*)com_android_internal_os_RuntimeInit_nativeFinishInit},
                {"nativeSetExitWithoutCleanup", "(Z)V",
                 (void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup},
        };
        // jniRegisterNativeMethods最终调用JNI动态注册函数 env->RegisterNatives(clazz, methods, numMethods);
        return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
            methods, NELEM(methods));
    }
    
    
    
    /**************libnativehelper/include/nativehelper/JNIHelp.h************************/
    
    [[maybe_unused]] static int jniRegisterNativeMethods(JNIEnv* env, const char* className,
                                                         const JNINativeMethod* methods,
                                                         int numMethods) {
        using namespace android::jnihelp;
        jclass clazz = env->FindClass(className);
        if (clazz == NULL) {
            __android_log_assert("clazz == NULL", "JNIHelp",
                                 "Native registration unable to find class '%s'; aborting...",
                                 className);
        }
        // JNI的接口注册Java函数
        int result = env->RegisterNatives(clazz, methods, numMethods);
        env->DeleteLocalRef(clazz);
        if (result == 0) {
            return 0;
        }
        ...
        ...
        return result;
    }
    

    在虚拟机创建并运行之后,Android注册了系统默认的Java native方法,此时Java的代码还未运行,这是为进入Java世界之后native方法被调用做准备。

    进入Java层

    在JNI函数表成功加载,虚拟机创建完毕,JNI函数注册完成之后,进入Java的逻辑其实特别简单,就是通过类的全路径名找到对应的类,然后callStaticVoidMethod,就从cpp进入了ZygoteInit.java的main方法中了。

    虚拟机如何建立Java到cpp的映射

    至此,我们可以说cpp已经建立起了和Java的联系了,后续Java可以通过定义的native方法调用到cpp中映射的方法了。但是我们难免还有些疑惑或者好奇,就是是在哪里建立起了这种映射关系?答案就是JNI的注册接口RegisterNatives,其实这是JNI规范的重要接口,理论上它的实现因虚拟机而异,开发者不需要关系,我们只需要知道当我们调用了注册接口之后,cpp的函数就和Java对应的方法建立起了映射关系,调用Java native方法就会准确的调用到cpp的对应函数。

    但是毕竟来都来了,再看看ART中关于JNI的部分实现也不是太复杂,不需要什么铺垫。

    
    /************************jni.h *********************************************/
    
     // 这是声明在jni头文件里的注册函数
     jint  (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,jint);
    
    
    
    
    /*************************** jni_internal.cc ***********************************/
    // 我们找到注册函数对应的定义实现
     static jint RegisterNatives(JNIEnv* env,
                                  jclass java_class,
                                  const JNINativeMethod* methods,
                                  jint method_count) {
       ...
       ...
       // class_linker 是art中用于加载 链接,解析Java字节码的类
       // 我们常说类的加载过程有三步:加载,链接,初始化,ClassLinker就是负责链接的部分
       // classlinker的主要工作是验证、准备和解析
        ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
        ...
        ...
        
        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;
          if (UNLIKELY(name == nullptr)) {
            ReportInvalidJNINativeMethod(soa, c.Get(), "method name", i);
            return JNI_ERR;
          } else if (UNLIKELY(sig == nullptr)) {
            ReportInvalidJNINativeMethod(soa, c.Get(), "method signature", i);
            return JNI_ERR;
          } else if (UNLIKELY(fnPtr == nullptr)) {
            ReportInvalidJNINativeMethod(soa, c.Get(), "native function", i);
            return JNI_ERR;
          }
         
         // ArtMethod是Java 方法在虚拟机中运行时的表示
          ArtMethod* m = nullptr;
          bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(env)->GetVm()->IsCheckJniEnabled();
          for (ObjPtr<mirror::Class> current_class = c.Get();
               current_class != nullptr;
               current_class = current_class->GetSuperClass()) {
            // 通过class methodName signature三个参数来定位Java的方法
            // <true>表示只在标识为native的方法中搜索
            m = FindMethod<true>(current_class, name, sig);
            // 找到就退出循环
            if (m != nullptr) {
              break;
            }
    
            // false 表示在非native方法中寻找
            m = FindMethod<false>(current_class, name, sig);
            if (m != nullptr) {
              break;
            }
          }
        // 如果m==null表示没找到Java层中定义的本地方法,返回错误
          if (m == nullptr) {
            ...
            ...
            return JNI_ERR;
          } else if (!m->IsNative()) { // 找到了但是不是native标识的,也返回错误
            ...
            ...
            return JNI_ERR;
          }
    //走到这里来,说明找到了Java层对应的native方法了
        ...
        ...
        //调用class_linker.RegisterNative 把Java对应的方法m和cpp定义的函数fnPtr建立对应关系
          const void* final_function_ptr = class_linker->RegisterNative(soa.Self(), m, fnPtr);
          
          UNUSED(final_function_ptr);
        }
        return JNI_OK;
      }
      
      /**************************classlinker.cc **************************/
      const void* ClassLinker::RegisterNative(
        Thread* self, ArtMethod* method, const void* native_method) {
    ...
    ...
      if (method->IsCriticalNative()) {
        MutexLock lock(self, critical_native_code_with_clinit_check_lock_);
        // Remove old registered method if any.
        auto it = critical_native_code_with_clinit_check_.find(method);
        if (it != critical_native_code_with_clinit_check_.end()) {
          critical_native_code_with_clinit_check_.erase(it);
        }
        // To ensure correct memory visibility, we need the class to be visibly
        // initialized before we can set the JNI entrypoint.
        if (method->GetDeclaringClass()->IsVisiblyInitialized()) {
          method->SetEntryPointFromJni(new_native_method);
        } else {
          critical_native_code_with_clinit_check_.emplace(method, new_native_method);
        }
      } else {
        method->SetEntryPointFromJni(new_native_method); // 设置native函数调用点
      }
      return new_native_method;
    }
      
      
      /****************************** art_method.h *************************************/
      // 这是ArtMethod内部的一个属性 
        // Must be the last fields in the method.
      struct PtrSizedFields {
        // Depending on the method type, the data is
        //   - native method: pointer to the JNI function registered to this method
        //                    or a function to resolve the JNI function,
        //   - resolution method: pointer to a function to resolve the method and
        //                        the JNI function for @CriticalNative.
        //   - conflict method: ImtConflictTable,
        //   - abstract/interface method: the single-implementation if any,
        //   - proxy method: the original interface method or constructor,
        //   - default conflict method: null
        //   - other methods: during AOT the code item offset, at runtime a pointer
        //                    to the code item.
        void* data_;  //假如是native方法,data_会指向映射的cpp层定义的函数指针
    
        // Method dispatch from quick compiled code invokes this pointer which may cause bridging into
        // the interpreter.
        // Java方法的入口
        void* entry_point_from_quick_compiled_code_; // 
      } ptr_sized_fields_;
    
    

    根据上面的分析我们可知,Java虚拟机加载了字节码之后,就可以在运行时轻易的找到对应的类和方法,然后让它和cpp的函数建立映射关系即可。

    Java正常如何调用到cpp

    由于Android的application是以Java为主要语言的,所以在cpp层进入Java层之后,逻辑的驱动就以Java为主了。

    当我们正常启动一个app时。可能会在某个时刻需要调用native方法,但是我们知道其实app启动完成之前,cpp层已经完全保存好了Java层与cpp层的系统层面必要的方法映射关系了。但是开发者也会有一些自定义的native方法需要建立映射,而这个过程只能是在app启动之后进行了。

    由于cpp层在开机时以及应用进程创建时就做好了一切准备工作,所以从Java层到cpp层的调用过程就没有那么复杂了:

    • 提前加载我们打包好的so库
    • 调用自定义的native方法

    加载库的逻辑

    从第一步起,代码如下

    
    public class Some{
    
        static{
            // 加载我们打包的lib.so库
             System.loadLibrary("lib")
            
        }
    }
    
    
    
    /******************* NativeFun.java *****************************/
    
    
    public class NativeFun {
    
        native String  callFromJavaAdd(int x,int y);
    
        native String  callFromJavaStr(String content,int v);
    
    
        public void callFromCpp(String content){
            
        }
    
    }
    
    

    而loadLibrary方法经过如下调用

    
    System.loadLibrary(String libname) 
    ---Runtime.loadLibrary0(libName,classLoader); 
    ------Runtime.nativeLoad(name,loader,ldLibraryPath);     
    
    // 最终调用到一个native方法中
    Runtime{
            ...
            ...
            // 这是一个系统定义的native 方法,我们直到开机时系统已经把这个方法在JNI中注册过了
            private static native String nativeLoad(String filename, ClassLoader loader, Class<?> caller);
    
    }
    

    这个也是系统提供的JNI方法,实现在核心库libcore.so中,而且在我们使用时已经注册好了,

    
    /*****************************Runtime.c*****************************************/
    
    //Runtime.nativeLoad方法所映射的cpp函数 Runtime_nativeLoad
    JNIEXPORT jstring JNICALL
    Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
                       jobject javaLoader, jclass caller)
    {
        return JVM_NativeLoad(env, javaFilename, javaLoader, caller);
    }
    
    // 注册逻辑
    static JNINativeMethod gMethods[] = {
      FAST_NATIVE_METHOD(Runtime, freeMemory, "()J"),
      FAST_NATIVE_METHOD(Runtime, totalMemory, "()J"),
      FAST_NATIVE_METHOD(Runtime, maxMemory, "()J"),
      NATIVE_METHOD(Runtime, nativeGc, "()V"),
      NATIVE_METHOD(Runtime, nativeExit, "(I)V"),
      // NATIVE_METHOD是一个宏定义,最终结果是
      //{"nativeLoad","(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Class;)",Runtime_native}
      // 一个JNINativeMethod类型的元素
      NATIVE_METHOD(Runtime, nativeLoad,
                    "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Class;)"
                        "Ljava/lang/String;"),
    };
    // 注册在libcore.so的JNI_OnLoad方法中
    void register_java_lang_Runtime(JNIEnv* env) {
      jniRegisterNativeMethods(env, "java/lang/Runtime", gMethods, NELEM(gMethods));
    }
    
    
    
    
    /****************************************** OpenjdkJvm.cc ************************************/
    
    JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
                                     jstring javaFilename,
                                     jobject javaLoader,
                                     jclass caller) {
      ScopedUtfChars filename(env, javaFilename);
      if (filename.c_str() == nullptr) {
        return nullptr;
      }
    
      std::string error_msg;
      {
        art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
        // 加载本地库
        bool success = vm->LoadNativeLibrary(env,
                                             filename.c_str(),
                                             javaLoader,
                                             caller,
                                             &error_msg);
        if (success) {
          return nullptr;
        }
      }
    ...
      return env->NewStringUTF(error_msg.c_str());
    }
    
    

    本地库的加载最终调用了虚拟机的LoadNativeLibrary函数。

    
    /************************* art/runtime/jni/java_vm_ext.cc ****************/
    bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
                                      const std::string& path,
                                      jobject class_loader,
                                      jclass caller_class,
                                      std::string* error_msg) {
        ...
        ...
    
        const char* path_str = path.empty() ? nullptr : path.c_str();
        bool needs_native_bridge = false;
        char* nativeloader_error_msg = nullptr;
        //最终调用dlopen系统调用,打开给定的库
        void* handle = android::OpenNativeLibrary(
                env,
                runtime_->GetTargetSdkVersion(),
                path_str,
                class_loader,
                (caller_location.empty() ? nullptr : caller_location.c_str()),
                library_path.get(),
                &needs_native_bridge,
                &nativeloader_error_msg);
    
        ...
        ...
        bool was_successful = false;
        // 最终调用dlsym系统调用,找到该so中的JNI_OnLoad方法
        void* sym = library->FindSymbol("JNI_OnLoad", nullptr, android::kJNICallTypeRegular);
        if (sym == nullptr) {
            VLOG(jni) << "[No JNI_OnLoad found in "" << path << ""]";
            was_successful = true;
        } else {
            ...
            ...
            using JNI_OnLoadFn = int(*)(JavaVM*, void*);
            JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
            // 调用JNI_OnLoad()函数
            int version = (*jni_on_load)(this, nullptr);
            ...
            ...
        }
    
        library->SetResult(was_successful);
        return was_successful;
    }
    
    

    这里有必要解释下dlopen和dlsym这个系统调用:

    • dlopen: 以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程
    • dlsym: 通过dlopen返回的句柄和连接符名称获取函数名或者变量名
      此外还有其他对应的系统调用:
    • dlerror:返回出现的错误
    • dlclose: 用来卸载打开的库

    OpenNativeLibrary通过调用dlopen打开我们传入的so库。紧接着我们通过FindSymbol间接调用了dlsym,找到so中的JNI_OnLoad函数。而我们的动态注册一般就放在这个函数中。

    不过我们还有静态注册的方式来建立映射关系,在so的加载阶段并没有做什么处理,正如之前文章所说,静态注册依赖JNI规定的固定格式的函数名来定位函数,因此只需要在Java层调用native函数时,动态查找对应的函数即可。

    Java调用native函数

    我们还是从开始的demo中说起,loadlibrary之后,我们可以正常的在合适的地方调用我们的native函数。当我们调用了native函数之后会发生什么呢?

    在Java调用函数之前,虚拟机加载class时,会链接它,从中解析出对应的native函数(也是ArtMethod对象),然后为这个native函数设置好JNI调用入口:

    /******************************* class_linker.cc ************************************/
    static void LinkCode(ClassLinker* class_linker,
                         ArtMethod* method,
                         const OatFile::OatClass* oat_class,
                         uint32_t class_def_method_index) REQUIRES_SHARED(Locks::mutator_lock_) {
    ...
    ...
    
      if (method->IsNative()) {
        // Set up the dlsym lookup stub. Do not go through `UnregisterNative()`
        // as the extra processing for @CriticalNative is not needed yet.
        // 我们自定义的JNI函数一般IsCriticalNative()是false
        // GetJniDlsymLookupStub()内联到了 art_jni_dlsym_lookup_stub
        method->SetEntryPointFromJni(
            method->IsCriticalNative() ? GetJniDlsymLookupCriticalStub() : GetJniDlsymLookupStub());
           
      }
    }
    
    
    /*************************** runtime_asm_entrypoints.h ***************************/
    
    // art_jni_dlsym_lookup_stub指令链接在汇编代码中,
    extern "C" void* art_jni_dlsym_lookup_stub(JNIEnv*, jobject);
    static inline const void* GetJniDlsymLookupStub() {
      return reinterpret_cast<const void*>(art_jni_dlsym_lookup_stub);
    }
    
    
    /******************************* jni_entrypoints_arm64.S*************************************/
    
    // 汇编代码调用artFindNativeMethod
        /*
         * Jni dlsym lookup stub.
         */
        .extern artFindNativeMethod
        .extern artFindNativeMethodRunnable
    ENTRY art_jni_dlsym_lookup_stub
        // spill regs.
        SAVE_ALL_ARGS_INCREASE_FRAME 2 * 8
        stp   x29, x30, [sp, ALL_ARGS_SIZE]
        .cfi_rel_offset x29, ALL_ARGS_SIZE
        .cfi_rel_offset x30, ALL_ARGS_SIZE + 8
        add   x29, sp, ALL_ARGS_SIZE
    
        mov x0, xSELF   // pass Thread::Current()
        // Call artFindNativeMethod() for normal native and artFindNativeMethodRunnable()
        // for @FastNative or @CriticalNative.
        ldr   xIP0, [x0, #THREAD_TOP_QUICK_FRAME_OFFSET]      // uintptr_t tagged_quick_frame
        bic   xIP0, xIP0, #TAGGED_JNI_SP_MASK                 // ArtMethod** sp
        ldr   xIP0, [xIP0]                                    // ArtMethod* method
        ldr   xIP0, [xIP0, #ART_METHOD_ACCESS_FLAGS_OFFSET]   // uint32_t access_flags
        mov   xIP1, #(ACCESS_FLAGS_METHOD_IS_FAST_NATIVE | ACCESS_FLAGS_METHOD_IS_CRITICAL_NATIVE)
        tst   xIP0, xIP1
        b.ne  .Llookup_stub_fast_or_critical_native
        bl    artFindNativeMethod
        b     .Llookup_stub_continue
        .Llookup_stub_fast_or_critical_native:
        bl    artFindNativeMethodRunnable
    

    类加载阶段对方法进行了解析,给native方法指定了一个调用函数art_jni_dlsym_lookup_stub,而这个函数会链接到一段汇编代码 ,再从汇编代码跳转到artFindNativeMethod方法,去寻找它对应的cpp函数

    /*****************************jni_entrypoints.cc************************/
    
    
    
    extern "C" const void* artFindNativeMethod(Thread* self) {
      DCHECK_EQ(self, Thread::Current());
      Locks::mutator_lock_->AssertNotHeld(self);  // We come here as Native.
      ScopedObjectAccess soa(self);
      return artFindNativeMethodRunnable(self);
    }
    
    extern "C" const void* artFindNativeMethodRunnable(Thread* self)
        REQUIRES_SHARED(Locks::mutator_lock_) {
      Locks::mutator_lock_->AssertSharedHeld(self);  // We come here as Runnable.
      uint32_t dex_pc;
      ArtMethod* method = self->GetCurrentMethod(&dex_pc); // 找到当前正在调用的方法
      DCHECK(method != nullptr);
      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
    
    
      // Check whether we already have a registered native code.
      // 先检查method中是否有我们动态注册的JNI函数,如果是静态注册,将找不到
      const void* native_code = class_linker->GetRegisteredNative(self, method);
      if (native_code != nullptr) {
        return native_code;
      }
    
      // Lookup symbol address for method, on failure we'll return null with an exception set,
      // otherwise we return the address of the method we found.
      JavaVMExt* vm = down_cast<JNIEnvExt*>(self->GetJniEnv())->GetVm();
      std::string error_msg;
      // 尝试查找静态注册的函数
      native_code = vm->FindCodeForNativeMethod(method, &error_msg, /*can_suspend=*/ true);
      if (native_code == nullptr) {
        LOG(ERROR) << error_msg;
        self->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;", error_msg.c_str());
        return nullptr;
      }
    
      // 注册当前JNI函数,并返回函数指针
      return class_linker->RegisterNative(self, method, native_code);
    }
    
    
    
    /********************* java_vm_ext.cc *********************************/
    
    
    
    void* JavaVMExt::FindCodeForNativeMethod(ArtMethod* m, std::string* error_msg, bool can_suspend) {
      CHECK(m->IsNative());
      ObjPtr<mirror::Class> c = m->GetDeclaringClass();
      // If this is a static method, it could be called before the class has been initialized.
      CHECK(c->IsInitializing() || !m->NeedsClinitCheckBeforeCall())
          << c->GetStatus() << " " << m->PrettyMethod();
      Thread* const self = Thread::Current();
      // 从本地已加载库的库中去查找
      void* native_method = libraries_->FindNativeMethod(self, m, error_msg, can_suspend);
      if (native_method == nullptr && can_suspend) {
        // Lookup JNI native methods from native TI Agent libraries. See runtime/ti/agent.h for more
        // information. Agent libraries are searched for native methods after all jni libraries.
        native_method = FindCodeForNativeMethodInAgents(m);
      }
      return native_method;
    }
    
    
    void* FindNativeMethod(Thread* self, ArtMethod* m, std::string* detail, bool can_suspend)
          REQUIRES(!Locks::jni_libraries_lock_)
          REQUIRES_SHARED(Locks::mutator_lock_) {
          // JniShortName是指静态注册是不包含参数类型的函数名
        std::string jni_short_name(m->JniShortName());
        // // JniLongName是指静态注册包含参数类型的函数名
        std::string jni_long_name(m->JniLongName());
        ...
        ...
        void* native_code = nullptr;
        android::JNICallType jni_call_type =
            m->IsCriticalNative() ? android::kJNICallTypeCriticalNative : android::kJNICallTypeRegular;
        if (can_suspend) { // can_suspend传入参数为true
          ScopedThreadSuspension sts(self, ThreadState::kNative);
          //查找对应的JNI方法
          native_code = FindNativeMethodInternal(self,
                                                 declaring_class_loader_allocator,
                                                 shorty,
                                                 jni_short_name,
                                                 jni_long_name,
                                                 jni_call_type);
        } 
        ...
        ...
        // 返回JNI函数
        if (native_code != nullptr) {
          return native_code;
        }
        ...
        ...
      }
      //具体查找方式
      void* FindNativeMethodInternal(Thread* self,
                                     void* declaring_class_loader_allocator,
                                     const char* shorty,
                                     const std::string& jni_short_name,
                                     const std::string& jni_long_name,
                                     android::JNICallType jni_call_type)
          REQUIRES(!Locks::jni_libraries_lock_) {
        // 遍历当前缓存的library
        for (const auto& lib : libraries_) {
          SharedLibrary* const library = lib.second;
          const char* arg_shorty = library->NeedsNativeBridge() ? shorty : nullptr;
          //尝试用short_name从当前遍历的库中去查找函数
          void* fn = library->FindSymbol(jni_short_name, arg_shorty, jni_call_type);
          if (fn == nullptr) {
          //如果找不到的话,再尝试用long_name从当前遍历的库中去查找函数
            fn = library->FindSymbol(jni_long_name, arg_shorty, jni_call_type);
          }
          if (fn != nullptr) {
            //最终返回对应的函数
            return fn;
          }
        }
        return nullptr;
      }
      
    
    
    // 以下是构建JNI的静态函数的方式  
      /**************************** art_method.cc***************************************/
    std::string ArtMethod::JniShortName() {
      return GetJniShortName(GetDeclaringClassDescriptor(), GetName());
    }
    
    std::string ArtMethod::JniLongName() {
      std::string long_name;
      long_name += JniShortName();
      long_name += "__";
    
      std::string signature(GetSignature().ToString());
      signature.erase(0, 1);
      signature.erase(signature.begin() + signature.find(')'), signature.end());
    
      long_name += MangleForJni(signature);
    
      return long_name;
    }
    
    
    /*******************************descriptors_names.cc******************************************/
    
    std::string GetJniShortName(const std::string& class_descriptor, const std::string& method) {
      // Remove the leading 'L' and trailing ';'...
      std::string class_name(class_descriptor);
      CHECK_EQ(class_name[0], 'L') << class_name;
      CHECK_EQ(class_name[class_name.size() - 1], ';') << class_name;
      class_name.erase(0, 1);
      class_name.erase(class_name.size() - 1, 1);
    
      std::string short_name;
      short_name += "Java_";
      short_name += MangleForJni(class_name);
      short_name += "_";
      short_name += MangleForJni(method);
      return short_name;
    }
     
    
    

    至此关于Java调用JNI函数的全过程基本完成了,简单总结一下,就是在Java类加载阶段,虚拟机就会解析类的结构,同时为native函数指定调用的函数,当方法被调用时,会跳转到指定的函数,然后再对应的函数里找是否有已注册的已注册的JNI函数,假如没找到就用固定命名方式利用dlsym系统调用来查找函数。

    cpp正常如何调用到Java

    当我们理解了从Java如何调用到cpp的过程后,从cpp调用Java的过程就显得不是很复杂了。因为本身Java就运行在虚拟机的cpp代码之上,从cpp层是很容易找到运行时的Java方法的。

    // 假设我们定义了一个从cpp调用Java的方法,
    
    
    extern "C"
    JNIEXPORT void JNICALL call_java_method(JNIEnv *env,jobject obj) {
        const char *className = "com/example/applicationnative/NativeFun";
        jclass  clazz = env->FindClass(className);
        if (clazz == nullptr){
            return;
        }
        jmethodID method_id = env->GetMethodID(clazz,"callFromCpp","(Ljava/lang/String;)V");
        jstring content = env->NewStringUTF("来自cpp的问候");
        env->CallVoidMethod(obj,method_id,content);
        env->DeleteLocalRef(clazz);
        env->DeleteLocalRef(content);
    }
    
    

    你会发现,其实我们大概需要关注三个函数的实现即可,分别是:

    • FindClass 查找类
    • GetMethodID 查找方法
    • CallVoidMethod 调用方法

    findClass

    
    ObjPtr<mirror::Class> ClassLinker::FindClass(Thread* self,
                                                 const char* descriptor,
                                                 Handle<mirror::ClassLoader> class_loader) {
        ...
        ...
        // 从已经加载的类中寻找对应的类
        ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, class_loader.Get());
        if (klass != nullptr) {
            return EnsureResolved(self, descriptor, klass);
        }
        // 如果没找到,类又不是数组类I型那个,class_loader是null,就从boot class loader中去寻找
        if (descriptor[0] != '[' && class_loader == nullptr) {
            // Non-array class and the boot class loader, search the boot class path.
            ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
            if (pair.second != nullptr) {
                return DefineClass(self,
                                   descriptor,
                                   hash,
                                   ScopedNullHandle<mirror::ClassLoader>(),
                                   *pair.first,
                                   *pair.second);
            }
            ...
        }
        ObjPtr<mirror::Class> result_ptr;
        bool descriptor_equals;
        if (descriptor[0] == '[') { //如果是数组类型
            result_ptr = CreateArrayClass(self, descriptor, hash, class_loader);
            ...
            ..
        } else {
            // 从BaseDexClassLoader中寻找
            bool known_hierarchy =
                    FindClassInBaseDexClassLoader(self, descriptor, hash, class_loader, &result_ptr);
            ...
            ...
        }
        ...
        ...
    
        return result_ptr;
    }
    
    ObjPtr<mirror::Class> ClassLinker::LookupClass(Thread* self,
                                                   const char* descriptor,
                                                   size_t hash,
                                                   ObjPtr<mirror::ClassLoader> class_loader) {
        ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
        // 获取class_loader对应的class_table
        ClassTable* const class_table = ClassTableForClassLoader(class_loader);
        if (class_table != nullptr) {
            // 从table中查找对应的类
            ObjPtr<mirror::Class> result = class_table->Lookup(descriptor, hash);
            if (result != nullptr) {
                return result;
            }
        }
        return nullptr;
    }
    
    

    GetMethodID

    static jmethodID GetMethodID(JNIEnv* env, jclass java_class, const char* name, const char* sig) {
         ...
         ...
        return FindMethodID<kEnableIndexIds>(soa, java_class, name, sig, false);
    }
    
    // 模板函数
    template<bool kEnableIndexIds>
    static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class,
                                  const char* name, const char* sig, bool is_static)
    REQUIRES_SHARED(Locks::mutator_lock_) {
    // 调用FindMethodJNI函数
            return jni::EncodeArtMethod<kEnableIndexIds>(FindMethodJNI(soa, jni_class, name, sig, is_static));
    }
    
    
    
    ArtMethod* FindMethodJNI(const ScopedObjectAccess& soa,
                             jclass jni_class,
                             const char* name,
                             const char* sig,
                             bool is_static) {
        // 获取初始化之后的类对象
        ObjPtr<mirror::Class> c = EnsureInitialized(soa.Self(), soa.Decode<mirror::Class>(jni_class));
        if (c == nullptr) {
            return nullptr;
        }
        ArtMethod* method = nullptr;
        auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
        if (c->IsInterface()) {
            method = c->FindInterfaceMethod(name, sig, pointer_size);
        } else {
            // 我们调用的类一般不是接口,调用FindClassMethod
            method = c->FindClassMethod(name, sig, pointer_size);
        }
        ...
        ...
        return method;
    }
    
    
    // FindClassMethod最终调用到这个模板函数
    template <typename SignatureType>
    static inline ArtMethod* FindClassMethodWithSignature(ObjPtr<Class> this_klass,
                                                          std::string_view name,
                                                          const SignatureType& signature,
                                                          PointerSize pointer_size)
    REQUIRES_SHARED(Locks::mutator_lock_) {
            // Search declared methods first.
            //从类中逐个扫描
            for (ArtMethod& method : this_klass->GetDeclaredMethodsSlice(pointer_size)) {
                ArtMethod* np_method = method.GetInterfaceMethodIfProxy(pointer_size);
                // 对比函数名,函数签名,两者一致就返回
                if (np_method->GetNameView() == name && np_method->GetSignature() == signature) {
                
                    return &method;
                }
            }
    
            ...
            ...
            return uninherited_method;  // Return the `uninherited_method` if any.
    }
    

    CallVoidMethod

    当可以找到Java运行时的方法所对应的ArtMethod对象的ID之后,就是调用方法了。

    /************************** art/runtime/reflection.cc ***************************/
    template <>
    JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
                                               jobject obj,
                                               jmethodID mid,
                                               va_list args) {
        // 把methodid解码为ArtMethod指针 调用InvokeVirtualOrInterfaceWithVarArgs
        return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, jni::DecodeArtMethod(mid), args);
    }
    
    template <>
    JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
                                               jobject obj,
                                               ArtMethod* interface_method,
                                               va_list args) {
        ...
        ...
        uint32_t shorty_len = 0;
        const char* shorty =
                method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(&shorty_len);
        JValue result;
        ArgArray arg_array(shorty, shorty_len);
        arg_array.BuildArgArrayFromVarArgs(soa, receiver, args);
        InvokeWithArgArray(soa, method, &arg_array, &result, shorty);
        if (is_string_init) {
            // For string init, remap original receiver to StringFactory result.
            UpdateReference(soa.Self(), obj, result.GetL());
        }
        return result;
    }
    
    void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa,
                            ArtMethod* method, ArgArray* arg_array, JValue* result,
                            const char* shorty)
    REQUIRES_SHARED(Locks::mutator_lock_) {
            uint32_t* args = arg_array->GetArray();
    
            // 调用Java方法
            method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty);
    }
    
    

    总结

    关于JNI的实现,我们大概按照三个阶段来分析的,启动阶段的JNI构建阶段,正常运行时Java_call_JNI的阶段,以及在jni_call_java的阶段

    构建阶段,在系统启动过程中,系统分加载jni相关的核心库,并导入相关的函数实现,同时创建虚拟机对象,并启动虚拟机,注册与此相关的JNI函数。

    运行时java_call_jni app进程初始化时,系统会加载类时会对native方法进行解析,把native方法都指向一个固定的函数,然后链接到一段汇编代码中
    Java层调用native方法前则需要先加载so库,系统会调用JNI_OnLoad函数,一般我们会在这个回调中添加注册JNI函数的方法,因此JNI_OnLoad也可能会触发注册函数相关的逻辑。在调用native方法后,我们会根据class链接时指向的调用路径,最终来到一段固定的汇编代码,通过artFindNativeMethod找到并执行注册的JNI函数。

    JNI_call_java 从JNI call Java时,一般一切环境都已经准备好了,虚拟机通过classname找到jclass,再利用函数信息找到运行时Java方法所对应的ArtMethod的id,接着通过这些信息调用函数即可。

    相关文章

      网友评论

          本文标题:JNI的源码实现分析

          本文链接:https://www.haomeiwen.com/subject/pdlitjtx.html