Art虚拟机中JavaVM &JNIEnv 的初始化流程

admin 2023年5月16日08:05:14评论86 views字数 7014阅读23分22秒阅读模式

1.前言

     在安卓jni编程中,常常接触到JavaVMJNIEnv。比如jni动态注册中的JNI_OnLoad方法是我们需要实现的。使用的时候需要通过JavaVM获取JNIEnv。参考如下:

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL; if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { return -1;    }    LOGD("JNI Loaded"); return JNI_VERSION_1_6;}

     除了动态注册的时候,在编写javanative方法在c层的实现时候,会使用到JNIEnv中提供的很多jni接口函数,比如FindClassCallObjectMethod等。

     以下将通过源码中追踪JavaVM&JNIEnv创建和初始化流程分析理解JNI层的一个大概实现机制。


2.JavaVMJNIEnv创建流程分析

    安卓中zygote是所有Java层进程的鼻祖。zygote启动的时候会创建art虚拟机。JavaVMJNIEnv初始化的流程也会在zygote启动的时候完成。


2.1  zygote启动流程


       (1).init进程解析init.rc

       安卓系统用户空间第一个进程init进程启动的时候会解析init.rc文件来确认使用那个一个init.zygotexx.rc文件来启动zygote进程。init.rc中相关内容如下:

...import /init.${ro.zygote}.rc...

     由以上内容可知需要根据ro.zygote属性来确认加载具体的zygote启动配置的rc 文件来启动32位还是64位的zygote进程。在文件路径"systemcorerootdir"存在多个zygote启动的配置文件,如下所示:

      

Art虚拟机中JavaVM &JNIEnv 的初始化流程


   ro.zygote属性会有四种不同的取值:

  • zygote32:代表32位模式运行zygote进程

  • zygote32_64:代表32为主,64位为辅的方式启动zygote进程

  • zygote64:代表64位方式启动zygote进程

  • zygote64_32:代表64位为主,32位为辅的方式启动zygote进程

   

以下是一加3手机在安卓10系统中的ro.zygote属性的值。如下所示:

C:UsersQiang>adb shell getprop |findstr "ro.zygote"[ro.zygote]: [zygote64_32]

 根据以上ro.zygote值为zygote64_32,当前手机将会加载init.zygote64_32.rc文件。init.zygote64_32.rc内容如下:

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote    class main    priority -20    user root    group root readproc reserved_disk    ...
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload class main priority -20 user root ...

      该配置文件中配置启动了两个zygote进程,一个是执行app_process64,一个是执行app_process32。接下来分析app_process进程的相应逻辑代码。


  (2).app_process启动过程


     在安卓系统源码中,app_process64和app_process32使用的是同一份源代码。源码路径位于目录:

frameworksbasecmdsapp_process

     安卓系统在编译的时候根据Android.mk文件配置编译成了两个平台并重命名。如下是相应的Android.mk文件中配置情况。

LOCAL_PATH:= $(call my-dir)...include $(CLEAR_VARS)...# 指定编译模块名称app_processLOCAL_MODULE:= app_process# 指定编译手机平台both表示32和64都要都要编译LOCAL_MULTILIB := both# 指定32位情况下编译名称变为app_process32LOCAL_MODULE_STEM_32 := app_process32# 指定64位情况下编译名称变为app_process64LOCAL_MODULE_STEM_64 := app_process64...include $(BUILD_EXECUTABLE)...

      app_process模块中只有一个源文件app_main.cppapp_main.cpp文件中和zygote启动相关的逻辑代码如下:

//AppRuntime继承AndroidRuntime类class AppRuntime : public AndroidRuntime{   ...}//zygote进程入口函数int main(int argc, char* const argv[]){    ...    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));    ...    if (zygote) {        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);    }     ...}

   以上代码中关键逻辑变成了AndroidRuntime->start,接下来分析一下AndroidRuntime中的相关逻辑。

 

 2.2  AndroidRuntime中的处理流程分析


     AndroidRuntime类文件路径如下:

frameworksbasecorejniAndroidRuntime.cpp

    在该类中的start方法如下

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

      以上方法中调用了startVm方法创建JavaVM和JNIEnv。startVm中的关键逻辑如下:

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote){   ...   if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {        ALOGE("JNI_CreateJavaVM failedn");        return -1;    }   ...}

     以上方法中执行JNI_CreateJavaVM方法来创建虚拟机。接下来分析JNI_CreateJavaVM中的处理逻辑。


2.3 JNI_CreateJavaVM中的处理流程分析


     JNI_CreateJavaVM方法定义在"jni.h"文件中。具体实现在文件"libnativehelperJniInvocation.cpp"中。该文件中JNI_CreateJavaVM实现如下:

MODULE_API jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {   ...  return JniInvocationImpl::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args);}

以上方法中使用了JniInvocationImpl类的JNI_CreateJavaVM方法。该类位于路径"libnativehelperJniInvocation.cpp"中,他的JNI_CreateJavaVM方法实现如下:

jint JniInvocationImpl::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {  return JNI_CreateJavaVM_(p_vm, p_env, vm_args);}

      在以上实现中调用了JNI_CreateJavaVM_方法,该方法是一个函数指针,定义为如下:

jint (*JNI_CreateJavaVM_)(JavaVM**, JNIEnv**, void*);

    在JniInvocationImpl类中函数指针JNI_CreateJavaVM_赋值相关逻辑实现如下:

//该方法主要是根据打开的libart.so来查找函数符号bool JniInvocationImpl::FindSymbol(FUNC_POINTER* pointer, const char* symbol) {  *pointer = GetSymbol(handle_, symbol);  ...  return true;}//bool JniInvocationImpl::Init(const char* library) {  ...  //获取需要加载的so,此处为libart.so  library = GetLibrary(library, buffer);  //打开libart.so  handle_ = OpenLibrary(library);  ...  //此处表示获取libart.so中的JNI_CreateJavaVM函数地址并赋值给JNI_CreateJavaVM_  if (!FindSymbol(reinterpret_cast<FUNC_POINTER*>(&JNI_CreateJavaVM_),                  "JNI_CreateJavaVM")) {    return false;  }  ...}

    以上分析可知最终调用的是libart.so中的JNI_CreateJavaVM方法来实现创建虚拟机。接下来分析art中的JNI_CreateJavaVM方法逻辑。


2.4 art中虚拟机创建分析

   在art源码中,JNI_CreateJavaVM方法实现源文件路径为:

artruntimejnijava_vm_ext.cc

   在该文件中JNI_CreateJavaVM的实现代码逻辑如下:

extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {  ...  //使用了Runtime::create创建虚拟机  if (!Runtime::Create(options, ignore_unrecognized)) {    return JNI_ERR;  }  ...  Runtime* runtime = Runtime::Current();  //启动虚拟机  bool started = runtime->Start();  ...  //初始化JNIEnv  *p_env = Thread::Current()->GetJniEnv();  //初始化JavaVM  *p_vm = runtime->GetJavaVM();  return JNI_OK;}

     以上代码分析核心流程变成了Runtime::Create。接下来分析art中的Runtime类中的处理逻辑。


2.5 Runtime中的处理逻辑

    

     Runtime类位于文件路径:

artruntimeruntime.cc

     在该类中Create调用的处理逻辑如下:

//调用1bool Runtime::Create(const RuntimeOptions& raw_options, bool ignore_unrecognized) {  RuntimeArgumentMap runtime_options;  return ParseOptions(raw_options, ignore_unrecognized, &runtime_options) &&      Create(std::move(runtime_options));}//调用2bool Runtime::Create(RuntimeArgumentMap&& runtime_options) {    ...  instance_ = new Runtime;  ...  //调用了Init方法初始化  if (!instance_->Init(std::move(runtime_options))) {    ...  }  return true;}

    以上调用中流程走到Init方法中,Init方法实现如下:

bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {  ...  std::string error_msg;  //这个地方真正创建JavaVM  java_vm_ = JavaVMExt::Create(this, runtime_options, &error_msg);  if (java_vm_.get() == nullptr) {    LOG(ERROR) << "Could not initialize JavaVMExt: " << error_msg;    return false;  }  ...  // 这个地方线程附加  Thread* self = Thread::Attach("main", false, nullptr, false);  ...  }

   以上逻中完成JavaVM的初始化,接下来继续看一下JNIEnv的初始化流程。


2.6  Thread类中的分析


   Thread类源码路径:

artruntimeruntime.cc

    在该类中Attach方法逻辑如下:

//调用1Thread* Thread::Attach(const char* thread_name,                       bool as_daemon,                       jobject thread_group,                       bool create_peer) {      ...      //调用另一个Attach return Attach(thread_name, as_daemon, create_peer_action); } //调用2 template <typename PeerAction>Thread* Thread::Attach(const char* thread_name, bool as_daemon, PeerAction peer_action) {  Runtime* runtime = Runtime::Current();  ...  Thread* self;  {    ...    if (runtime->IsShuttingDownLocked()) {     ...    } else {      Runtime::Current()->StartThreadBirth();      self = new Thread(as_daemon);      //初始化JNIEnv相关逻辑      bool init_success = self->Init(runtime->GetThreadList(), runtime->GetJavaVM());      ...    }  } ...}

    以上代码中核心进入Thread->Init方法,Init方法实现如下:

bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm, JNIEnvExt* jni_env_ext) {  ...  if (jni_env_ext != nullptr) {   ...  } else {    std::string error_msg;    //创建JNIEnv    tlsPtr_.jni_env = JNIEnvExt::Create(this, java_vm, &error_msg);    if (tlsPtr_.jni_env == nullptr) {      LOG(ERROR) << "Failed to create JNIEnvExt: " << error_msg;      return false;    }  }  ...}


3.总结


  JavaVM创建关键:

JavaVMExt::Create(this, runtime_options, &error_msg);

  JNIEnv创建关键:

JNIEnvExt::Create(this, java_vm, &error_msg);

 


    本篇主要分析JavaVM&JNIEnv在zygote启动过程中的创建和初始化后面会通过JNIEnv->FindClass的接口调用分析JNI相关接口的内部实现机制。


Art虚拟机中JavaVM &JNIEnv 的初始化流程

原文始发于微信公众号(哆啦安全):Art虚拟机中JavaVM &JNIEnv 的初始化流程

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年5月16日08:05:14
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Art虚拟机中JavaVM &JNIEnv 的初始化流程http://cn-sec.com/archives/787014.html

发表评论

匿名网友 填写信息