-
-
[分享]Art虚拟机JNIEnv相关分析(1)JavaVM &JNIEnv 的初始化流程
-
2021-4-14 10:52 7664
-
1.前言
在安卓jni编程中,常常接触到JavaVM和JNIEnv。比如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; }
除了动态注册的时候,在编写java层native方法在c层的实现时候,会使用到JNIEnv中提供的很多jni接口函数,比如FindClass、CallObjectMethod等。
以下将通过源码中追踪JavaVM&JNIEnv创建和初始化流程分析理解JNI层的一个大概实现机制。
2.JavaVM和JNIEnv创建流程分析
安卓中zygote是所有Java层进程的鼻祖。zygote启动的时候会创建art虚拟机。JavaVM和JNIEnv初始化的流程也会在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进程。在文件路径"system\core\rootdir"存在多个zygote启动的配置文件,如下所示:
ro.zygote属性会有四种不同的取值:
zygote32:代表32位模式运行zygote进程
zygote32_64:代表32为主,64位为辅的方式启动zygote进程
zygote64:代表64位方式启动zygote进程
zygote64_32:代表64位为主,32位为辅的方式启动zygote进程
以下是一加3手机在安卓10系统中的ro.zygote属性的值。如下所示:
C:\Users\Qiang>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使用的是同一份源代码。源码路径位于目录:
frameworks\base\cmds\app_process
安卓系统在编译的时候根据Android.mk文件配置编译成了两个平台并重命名。如下是相应的Android.mk文件中配置情况。
LOCAL_PATH:= $(call my-dir) ... include $(CLEAR_VARS) ... # 指定编译模块名称app_process LOCAL_MODULE:= app_process # 指定编译手机平台both表示32和64都要都要编译 LOCAL_MULTILIB := both # 指定32位情况下编译名称变为app_process32 LOCAL_MODULE_STEM_32 := app_process32 # 指定64位情况下编译名称变为app_process64 LOCAL_MODULE_STEM_64 := app_process64 ... include $(BUILD_EXECUTABLE) ...
app_process模块中只有一个源文件app_main.cpp。app_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类文件路径如下:
frameworks\base\core\jni\AndroidRuntime.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中的关键逻辑如下:
{
...
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
ALOGE("JNI_CreateJavaVM failed\n");
return -1;
}
...
}
以上方法中执行JNI_CreateJavaVM方法来创建虚拟机。接下来分析JNI_CreateJavaVM中的处理逻辑。
2.3 JNI_CreateJavaVM中的处理流程分析
JNI_CreateJavaVM方法定义在"jni.h"文件中。具体实现在文件"libnativehelper\JniInvocation.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方法。该类位于路径"libnativehelper\JniInvocation.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方法实现源文件路径为:
art\runtime\jni\java_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类位于文件路径:
art\runtime\runtime.cc
在该类中Create调用的处理逻辑如下:
//调用1 bool 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)); } //调用2 bool 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类源码路径:
art\runtime\runtime.cc
在该类中Attach方法逻辑如下:
//调用1 Thread* 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);