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进程。在文件路径"systemcorerootdir"存在多个zygote启动的配置文件,如下所示:
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_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类文件路径如下:
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调用的处理逻辑如下:
//调用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类源码路径:
artruntimeruntime.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);
本篇主要分析JavaVM&JNIEnv在zygote启动过程中的创建和初始化。后面会通过JNIEnv->FindClass的接口调用分析JNI相关接口的内部实现机制。
原文始发于微信公众号(哆啦安全):Art虚拟机中JavaVM &JNIEnv 的初始化流程
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论