Dalvik仮想マシンの起動プロセス
記事の出典:http://blog.csdn.net/shift_wwx
Zygoteプロセスは、起動中にDalvik仮想マシンインスタンスを作成するほか、Javaランタイムライブラリをプロセスにロードしたり、AndroidコアクラスのJNIメソッドを登録して前に作成したDalvik仮想マシンインスタンスに登録したりします.アプリケーション・プロセスがZygoteプロセスによってインキュベーションされると、ZygoteプロセスのDalvik仮想マシン・インスタンス・コピーだけでなく、Javaランタイム・ライブラリもZygoteと共有されます.これは、Linuxカーネルのプロセス作成メカニズム(fork)のおかげです.このZygoteインキュベーションメカニズムの利点は、アプリケーションプロセスを迅速に起動できるだけでなく、全体的なメモリ消費を節約できることです.欠点は、起動速度に影響します.結局、Zygoteは起動中に起動します.しかし、全体的にはメリットがデメリットよりも大きいです.結局、システム全体にZygoteプロセスが1つしかありません.無数のアプリケーションプロセスがある可能性があります.携帯電話を頻繁にオフにすることはありません.多くの場合、スリープ状態にするだけです.
zygote(android zygoteの起動プロセス分析)を起動すると、仮想マシンが作成され、詳細な記録が作成されます.
@/frameworks/base/cmds/app_process/app_main.cpp
1)変数runtime
2)runtime.start
@/frameworks/vase/core/jni/AndroidRuntime.cpp
1)印刷開始直後>>>>>>>Android Runtime START%s <<<<<<,ここの%sはzygoteなので、logcatの時に見られる言葉で、この時にzygoteが起動し始めたことを示しています
2)初回起動時にoptionsがstart-system-serverに等しいと判断
3)android_の設定rootパスは/system
4)仮想マシンの作成
もちろんここはlibdvm.so
startVm関数はあまり説明しませんが、注意しなければならないのは:
Controlling the Embedded VM
文字列の長さはPROPERTY_VALUE_MAXの制限.
このマクロ定義の場所を見てみるとわかります.
@system/core/include/cutils/properties.h
@/bionic/include/sys/system_properties.h
シリアルポートgetprop|grep dalvikで
JNI_CreateJavaVMは主に以下の4つのことを完了します. 1.現在のプロセスのDalvik仮想マシンインスタンス、すなわちJavaVMExtオブジェクトを作成します. 2.関数dvmCreateJNIEnvを呼び出すことによって、現在のスレッドのJNI環境、すなわちJNIEnvExtオブジェクトを作成および初期化します. 3.パラメータvm_をargsによって記述されたDalvik仮想マシン起動オプションは、変数argvによって記述された文字列配列にコピーされ、関数dvmStartupが呼び出され、前に作成されたDalvik仮想マシンインスタンスが初期化される. 4.呼び出し関数dvmChangeStatus現在のスレッドの状態をNATIVEコードを実行しているように設定し、面が作成して初期化されたJavaVMExtオブジェクトとJNIEnvExtオブジェクトを出力パラメータp_vmとp_Envは呼び出し元に返されます.gDvmは、現在のプロセスのすべての仮想マシンに関する情報を収集するDvmGlobalsタイプのグローバル変数です.メンバー変数vmListは、現在のプロセスのDalvik仮想マシンインスタンス、すなわちJavaVMExtオブジェクトを指します.その後、現在のプロセスにおけるDalvik仮想マシンインスタンスにアクセスする必要があるたびに、グローバル変数gDvmのメンバー変数vmListによって取得することができ、このDalvik仮想マシンインスタンスが関数間で伝達されることを回避することができる.各Dalvik仮想マシンインスタンスには、対応するJavaVMExtオブジェクトのメンバー変数funcTableに保存される関数テーブルがあり、この関数テーブルはgInvokeInterfaceとして指定されます.gInvokeInterfaceは、ファイルdalvik/vm/Jni.cに定義されたJNIInvokeInterfaceのタイプの構造体です.
詳細については、Dalvik仮想マシンの起動プロセス分析を参照してください.
5)AndroidコアクラスのJNI登録方法
Android Runtimeクラスのメンバー関数startRegは、関数register_を呼び出します.jni_ProcsはAndroidコアクラスのJNIメソッドを登録します.JNIメソッドを登録するには、現在のスレッドのNativeスタックに記録する必要があるJavaオブジェクトをNativeコードで参照する必要があります.しかし、この時点でDalvik仮想マシンはまだ実際に稼働していません.つまり、現在のスレッドのNativeスタックはまだ準備ができていません.この場合、JNIメソッドを登録する前に、現在のスレッドのNativeスタックに1フレーム(Frame)を手動で押し込み、JNIメソッドを登録した後、手動でフレームを弾き出す必要がある.
現在のスレッドのJNI環境は、パラメータenvが指すJNIEnvオブジェクトによって記述され、そのメンバー関数PushLocalFrameとPopLocalFrameを呼び出すことで、現在のスレッドのNativeスタックに手動でフレームを押し込み、ポップアップすることができます.なお、このフレームはローカルフレームであり、Nativeコード内のJavaオブジェクトのローカル参照を保存するためにのみ使用できます.
関数register_jni_procsは以下の通りです.
グローバル変数gRegJNIによって記述されたJNIメソッド登録関数テーブルを観察することで、以下に示すように、Androidコアクラスに登録されているJNIメソッドを見ることができます.
6)zygote main関数の起動
まとめ:
Dalvik仮想マシンのZygoteプロセスでの起動プロセスを分析しました.この起動プロセスは主に以下の4つのことを完了しました.1.Dalvik仮想マシンのインスタンスを作成しました.2.JavaコアクラスとそのJNIメソッドをロードした.3.メインスレッドのJNI環境が設定されている.4.AndroidコアクラスのJNIメソッドを登録しました.
すなわち、ZygoteプロセスはAndroidシステムのためにDalvik仮想マシンインスタンスを用意し、その後、ZygoteプロセスはAndroidアプリケーションプロセスを作成する際に、自身のDalvik仮想マシンインスタンスを新たにAndroidアプリケーションプロセスを作成することにコピーし、Androidアプリケーションプロセスの開始プロセスを加速させることができる.また、JavaコアクラスとAndroidコアクラス(dexファイルにある)、およびそれらのJNIメソッド(soファイルにある)は、いずれもメモリマッピングで読み取られるため、ZygoteプロセスはAndroidアプリケーションプロセスを作成する際に、自身のDalvik仮想マシンインスタンスを新しく作成したAndroidアプリケーションプロセスにコピーできるほか、また、JavaコアクラスとAndroidコアクラス、およびJNIメソッドを新しく作成したAndroidアプリケーションプロセスと共有することで、メモリ消費を節約できます.また、ZygoteプロセスはAndroidアプリケーションプロセスの起動プロセスを加速させるために、Javaコアクラスを大量にロードし、AndroidコアクラスJNIメソッドを大量に登録する必要があるため、自分の起動速度を犠牲にしていることも見られます.Dalvik仮想マシンは、Javaコアクラスをロードするときに検証および最適化する必要があります.これらは通常、時間がかかります.また、Zygoteプロセスはinitプロセスによって開始されるため、つまりZygoteプロセスは起動時に開始されるため、Zygoteプロセスの犠牲は比較的大きい.しかし、結局、私たちが携帯電話をいじっている間に、電源を切ることはめったにありません.つまり、電源を入れることは少ないので、Zygoteプロセスの起動速度を犠牲にするのは価値があります.代わりにAndroidアプリケーションの迅速な起動です.また、AndroidシステムはJavaクラスのロード速度を速めるために、Dalvik Optimization and Verification With dexoptを参照して、Dexファイルの検証と最適化を事前に行う方法も考えられます.
Zygoteプロセスは、起動中にDalvik仮想マシンインスタンスを作成するほか、Javaランタイムライブラリをプロセスにロードしたり、AndroidコアクラスのJNIメソッドを登録して前に作成したDalvik仮想マシンインスタンスに登録したりします.アプリケーション・プロセスがZygoteプロセスによってインキュベーションされると、ZygoteプロセスのDalvik仮想マシン・インスタンス・コピーだけでなく、Javaランタイム・ライブラリもZygoteと共有されます.これは、Linuxカーネルのプロセス作成メカニズム(fork)のおかげです.このZygoteインキュベーションメカニズムの利点は、アプリケーションプロセスを迅速に起動できるだけでなく、全体的なメモリ消費を節約できることです.欠点は、起動速度に影響します.結局、Zygoteは起動中に起動します.しかし、全体的にはメリットがデメリットよりも大きいです.結局、システム全体にZygoteプロセスが1つしかありません.無数のアプリケーションプロセスがある可能性があります.携帯電話を頻繁にオフにすることはありません.多くの場合、スリープ状態にするだけです.
zygote(android zygoteの起動プロセス分析)を起動すると、仮想マシンが作成され、詳細な記録が作成されます.
@/frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
#ifdef __arm__
/*
* b/7188322 - Temporarily revert to the compat memory layout
* to avoid breaking third party apps.
*
* THIS WILL GO AWAY IN A FUTURE ANDROID RELEASE.
*
* http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=7dbaa466
* changes the kernel mapping from bottom up to top-down.
* This breaks some programs which improperly embed
* an out of date copy of Android's linker.
*/
char value[PROPERTY_VALUE_MAX];
property_get("ro.kernel.qemu", value, "");
bool is_qemu = (strcmp(value, "1") == 0);
if ((getenv("NO_ADDR_COMPAT_LAYOUT_FIXUP") == NULL) && !is_qemu) {
int current = personality(0xFFFFFFFF);
if ((current & ADDR_COMPAT_LAYOUT) == 0) {
personality(current | ADDR_COMPAT_LAYOUT);
setenv("NO_ADDR_COMPAT_LAYOUT_FIXUP", "1", 1);
execv("/system/bin/app_process", argv);
return -1;
}
}
unsetenv("NO_ADDR_COMPAT_LAYOUT_FIXUP");
#endif
// These are global variables in ProcessState.cpp
mArgC = argc;
mArgV = argv;
mArgLen = 0;
for (int i=0; i<argc; i++) {
mArgLen += strlen(argv[i]) + 1;
}
mArgLen--;
AppRuntime runtime;
const char* argv0 = argv[0];
// Process command line arguments
// ignore argv[0]
argc--;
argv++;
// Everything up to '--' or first non '-' arg goes to the vm
int i = runtime.addVmArguments(argc, argv);
// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
bool startSystemServer = false;
bool application = false;
const char* parentDir = NULL;
const char* niceName = NULL;
const char* className = NULL;
// YUNOS CODEBASE BEGIN 2014-03-14 [email protected]
char system_server_firstboot[PROPERTY_VALUE_MAX];
// YUNOS CODEBASE END
while (i < argc) {
const char* arg = argv[i++];
if (!parentDir) {
parentDir = arg;
} else if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = "zygote";
} else if (strcmp(arg, "--start-system-server") == 0) {
// YUNOS CODEBASE BEGIN 2014-03-14 [email protected]
property_get("sys.systemserver.firstboot", system_server_firstboot, "0");
if (strcmp(system_server_firstboot, "1") == 0) {
ALOGE(">>>>>>>>>>>>>>>>>>>>>ANDROID DIED---REBOOT KERNEL<<<<<<<<<<<<<<<<<<<<<<<<<
");
sync();
usleep(3000*1000L);
__reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, (void*)"ANDROID");
} else {
property_set("sys.systemserver.firstboot", "1");
}
// YUNOS CODEBASE END
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName = arg + 12;
} else {
className = arg;
break;
}
}
if (niceName && *niceName) {
setArgv0(argv0, niceName);
set_process_name(niceName);
}
runtime.mParentDir = parentDir;
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit",
startSystemServer ? "start-system-server" : "");
} else if (className) {
// Remainder of args get passed to startup class main()
runtime.mClassName = className;
runtime.mArgC = argc - i;
runtime.mArgV = argv + i;
runtime.start("com.android.internal.os.RuntimeInit",
application ? "application" : "tool");
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.
");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
return 10;
}
}
注意:1)変数runtime
AppRuntime runtime;
このクラスはapp_main.cppには次の定義があります.class AppRuntime : public AndroidRuntime
{
public:
AppRuntime()
: mParentDir(NULL)
, mClassName(NULL)
, mClass(NULL)
, mArgC(0)
, mArgV(NULL)
{
}
...
...
const char* getClassName() const
{
return mClassName;
}
virtual void onVmCreated(JNIEnv* env)
{
if (mClassName == NULL) {
return; // Zygote. Nothing to do here.
}
/*
* This is a little awkward because the JNI FindClass call uses the
* class loader associated with the native method we're executing in.
* If called in onStarted (from RuntimeInit.finishInit because we're
* launching "am", for example), FindClass would see that we're calling
* from a boot class' native method, and so wouldn't look for the class
* we're trying to look up in CLASSPATH. Unfortunately it needs to,
* because the "am" classes are not boot classes.
*
* The easiest fix is to call FindClass here, early on before we start
* executing boot class Java code and thereby deny ourselves access to
* non-boot classes.
*/
char* slashClassName = toSlashClassName(mClassName);
mClass = env->FindClass(slashClassName);
if (mClass == NULL) {
ALOGE("ERROR: could not find class '%s'
", mClassName);
}
free(slashClassName);
mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));
}
virtual void onStarted()
{
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.
");
proc->startThreadPool();
AndroidRuntime* ar = AndroidRuntime::getRuntime();
ar->callMain(mClassName, mClass, mArgC, mArgV);
IPCThreadState::self()->stopProcess();
}
virtual void onZygoteInit()
{
// Re-enable tracing now that we're no longer in Zygote.
atrace_set_tracing_enabled(true);
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.
");
proc->startThreadPool();
}
virtual void onExit(int code)
{
if (mClassName == NULL) {
// if zygote
IPCThreadState::self()->stopProcess();
}
AndroidRuntime::onExit(code);
}
const char* mParentDir;
const char* mClassName;
jclass mClass;
int mArgC;
const char* const* mArgV;
};
AppRuntimeはAndroid Runtimeクラスを継承しており、Android RuntimeのonVmCreated、onZygoteInitなどはここで書き換えられます.2)runtime.start
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit",
startSystemServer ? "start-system-server" : "");
で渡された2つのパラメータ.1つはzygoteのjavaクラス名で、もう1つは後でsystem_を起動するために使用されます.server @/frameworks/vase/core/jni/AndroidRuntime.cpp
/*
* Start the Android runtime. This involves starting the virtual machine
* and calling the "static void main(String[] args)" method in the class
* named by "className".
*
* Passes the main function two arguments, the class name and the specified
* options string.
*/
void AndroidRuntime::start(const char* className, const char* options)
{
ALOGD("
>>>>>> AndroidRuntime START %s <<<<<<
",
className != NULL ? className : "(unknown)");
/*
* 'startSystemServer == true' means runtime is obsolete and not run from
* init.rc anymore, so we print out the boot start event here.
*/
if (strcmp(options, "start-system-server") == 0) {
/* track our progress through the boot sequence */
const int LOG_BOOT_PROGRESS_START = 3000;
LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
}
const char* rootDir = getenv("ANDROID_ROOT");
if (rootDir == NULL) {
rootDir = "/system";
if (!hasDir("/system")) {
LOG_FATAL("No root directory specified, and /android does not exist.");
return;
}
setenv("ANDROID_ROOT", rootDir, 1);
}
//const char* kernelHack = getenv("LD_ASSUME_KERNEL");
//ALOGD("Found LD_ASSUME_KERNEL='%s'
", kernelHack);
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env) != 0) {
return;
}
onVmCreated(env);
/*
* Register android functions.
*/
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives
");
return;
}
/*
* We want to call main() with a String array with arguments in it.
* At present we have two arguments, the class name and an option string.
* Create an array to hold them.
*/
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
jstring optionsStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(2, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
optionsStr = env->NewStringUTF(options);
env->SetObjectArrayElement(strArray, 1, optionsStr);
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
char* slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'
", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'
", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
free(slashClassName);
ALOGD("Shutting down VM
");
if (mJavaVM->DetachCurrentThread() != JNI_OK)
ALOGW("Warning: unable to detach main thread
");
if (mJavaVM->DestroyJavaVM() != 0)
ALOGW("Warning: VM did not shut down cleanly
");
}
注意:1)印刷開始直後>>>>>>>Android Runtime START%s <<<<<<,ここの%sはzygoteなので、logcatの時に見られる言葉で、この時にzygoteが起動し始めたことを示しています
2)初回起動時にoptionsがstart-system-serverに等しいと判断
3)android_の設定rootパスは/system
4)仮想マシンの作成
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env) != 0) {
return;
}
onVmCreated(env);
JniInvocationクラス@/libnativehelper/jniInvocation.cppJniInvocation::JniInvocation() :
handle_(NULL),
JNI_GetDefaultJavaVMInitArgs_(NULL),
JNI_CreateJavaVM_(NULL),
JNI_GetCreatedJavaVMs_(NULL) {
LOG_ALWAYS_FATAL_IF(jni_invocation_ != NULL, "JniInvocation instance already initialized");
jni_invocation_ = this;
}
JniInvocation::~JniInvocation() {
jni_invocation_ = NULL;
if (handle_ != NULL) {
dlclose(handle_);
}
}
bool JniInvocation::Init(const char* library) {
#ifdef HAVE_ANDROID_OS
char default_library[PROPERTY_VALUE_MAX];
property_get(kLibrarySystemProperty, default_library, kLibraryFallback);
#else
const char* default_library = kLibraryFallback;
#endif
if (library == NULL) {
library = default_library;
}
handle_ = dlopen(library, RTLD_NOW);
if (handle_ == NULL) {
if (strcmp(library, kLibraryFallback) == 0) {
// Nothing else to try.
ALOGE("Failed to dlopen %s: %s", library, dlerror());
return false;
}
// Note that this is enough to get something like the zygote
// running, we can't property_set here to fix this for the future
// because we are root and not the system user. See
// RuntimeInit.commonInit for where we fix up the property to
// avoid future fallbacks. http://b/11463182
ALOGW("Falling back from %s to %s after dlopen error: %s",
library, kLibraryFallback, dlerror());
library = kLibraryFallback;
handle_ = dlopen(library, RTLD_NOW);
if (handle_ == NULL) {
ALOGE("Failed to dlopen %s: %s", library, dlerror());
return false;
}
}
if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),
"JNI_GetDefaultJavaVMInitArgs")) {
return false;
}
if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
"JNI_CreateJavaVM")) {
return false;
}
if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),
"JNI_GetCreatedJavaVMs")) {
return false;
}
return true;
}
JniInvocationクラスのメンバー関数initは簡単です.まず、システム属性persist.sys.dalvik.vm.libの値を読み出します.システム属性persist.sys.dalvik.vm.libの値はlibdvm.soに等しいかlibart.soに等しいか、この2つのsoライブラリはそれぞれDalvik仮想マシンとART仮想マシン環境に対応しています.もちろんここはlibdvm.so
startVm関数はあまり説明しませんが、注意しなければならないのは:
char stackTraceFileBuf[PROPERTY_VALUE_MAX];
char dexoptFlagsBuf[PROPERTY_VALUE_MAX];
char enableAssertBuf[sizeof("-ea:")-1 + PROPERTY_VALUE_MAX];
char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX];
char heapstartsizeOptsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
char heapsizeOptsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
char heapgrowthlimitOptsBuf[sizeof("-XX:HeapGrowthLimit=")-1 + PROPERTY_VALUE_MAX];
char heapminfreeOptsBuf[sizeof("-XX:HeapMinFree=")-1 + PROPERTY_VALUE_MAX];
char heapmaxfreeOptsBuf[sizeof("-XX:HeapMaxFree=")-1 + PROPERTY_VALUE_MAX];
char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];
char jitcodecachesizeOptsBuf[sizeof("-Xjitcodecachesize:")-1 + PROPERTY_VALUE_MAX];
char extraOptsBuf[PROPERTY_VALUE_MAX];
これらの文字列はDalvikの起動オプションであり、詳細なパラメータは次のように参照できます.Controlling the Embedded VM
文字列の長さはPROPERTY_VALUE_MAXの制限.
このマクロ定義の場所を見てみるとわかります.
@system/core/include/cutils/properties.h
/* System properties are *small* name value pairs managed by the
** property service. If your data doesn't fit in the provided
** space it is not appropriate for a system property.
**
** WARNING: system/bionic/include/sys/system_properties.h also defines
** these, but with different names. (TODO: fix that)
*/
#define PROPERTY_KEY_MAX PROP_NAME_MAX
#define PROPERTY_VALUE_MAX PROP_VALUE_MAX
PROP_についてNAME_MAXとPROP_BALUE_MAXはinitの解析で@/bionic/include/sys/system_properties.h
#define PROP_NAME_MAX 32
#define PROP_VALUE_MAX 92
prop value長さが92を超えないことを要求シリアルポートgetprop|grep dalvikで
[dalvik.vm.heapgrowthlimit]: [64m]
[dalvik.vm.heapmaxfree]: [8m]
[dalvik.vm.heapminfree]: [512k]
[dalvik.vm.heapsize]: [384m]
[dalvik.vm.heapstartsize]: [8m]
[dalvik.vm.heaptargetutilization]: [0.75]
[dalvik.vm.stack-trace-file]: [/data/anr/traces.txt]
[persist.sys.dalvik.vm.lib]: [libdvm.so]
また、最後のcodeもstartVmの鍵です. /*
* Initialize the VM.
*
* The JavaVM* is essentially per-process, and the JNIEnv* is per-thread.
* If this call succeeds, the VM is ready, and we can start issuing
* JNI calls.
*/
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
ALOGE("JNI_CreateJavaVM failed
");
goto bail;
}
JNI_CreateJavaVMという関数はdalvik/vm/Jni.cppにあります.JNI_CreateJavaVMは主に以下の4つのことを完了します. 1.現在のプロセスのDalvik仮想マシンインスタンス、すなわちJavaVMExtオブジェクトを作成します. 2.関数dvmCreateJNIEnvを呼び出すことによって、現在のスレッドのJNI環境、すなわちJNIEnvExtオブジェクトを作成および初期化します. 3.パラメータvm_をargsによって記述されたDalvik仮想マシン起動オプションは、変数argvによって記述された文字列配列にコピーされ、関数dvmStartupが呼び出され、前に作成されたDalvik仮想マシンインスタンスが初期化される. 4.呼び出し関数dvmChangeStatus現在のスレッドの状態をNATIVEコードを実行しているように設定し、面が作成して初期化されたJavaVMExtオブジェクトとJNIEnvExtオブジェクトを出力パラメータp_vmとp_Envは呼び出し元に返されます.gDvmは、現在のプロセスのすべての仮想マシンに関する情報を収集するDvmGlobalsタイプのグローバル変数です.メンバー変数vmListは、現在のプロセスのDalvik仮想マシンインスタンス、すなわちJavaVMExtオブジェクトを指します.その後、現在のプロセスにおけるDalvik仮想マシンインスタンスにアクセスする必要があるたびに、グローバル変数gDvmのメンバー変数vmListによって取得することができ、このDalvik仮想マシンインスタンスが関数間で伝達されることを回避することができる.各Dalvik仮想マシンインスタンスには、対応するJavaVMExtオブジェクトのメンバー変数funcTableに保存される関数テーブルがあり、この関数テーブルはgInvokeInterfaceとして指定されます.gInvokeInterfaceは、ファイルdalvik/vm/Jni.cに定義されたJNIInvokeInterfaceのタイプの構造体です.
static const struct JNIInvokeInterface gInvokeInterface = {
NULL,
NULL,
NULL,
DestroyJavaVM,
AttachCurrentThread,
DetachCurrentThread,
GetEnv,
AttachCurrentThreadAsDaemon,
};
このDalvik仮想マシン関数テーブルがあれば、現在のスレッドAttachまたはDetachをDalvik仮想マシンに移動したり、現在のプロセスのDalvik仮想マシンを破棄したりすることができます.詳細については、Dalvik仮想マシンの起動プロセス分析を参照してください.
5)AndroidコアクラスのJNI登録方法
/*
* Register android functions.
*/
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives
");
return;
}
/*
* Register android native functions with the VM.
*/
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
/*
* 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);
ALOGV("--- registering native functions ---
");
/*
* Every "register" function calls one or more things that return
* a local reference (e.g. FindClass). Because we haven't really
* started the VM yet, they're all getting stored in the base frame
* and never released. Use Push/Pop to manage the storage.
*/
env->PushLocalFrame(200);
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
env->PopLocalFrame(NULL);
//createJavaThread("fubar", quickTest, (void*) "hello");
return 0;
}
AndroidRuntimeクラスのメンバー関数startRegは、まず関数androidSetCreateThreadFuncを呼び出して、スレッド作成フックjavaCreateThreadEtcを設定します.このスレッド作成フックは、NativeスレッドのJNI環境を初期化するために使用されます.すなわち、C++コードにNativeスレッドを作成すると、関数javaCreateThreadEtcが呼び出され、NativeスレッドのJNI環境を初期化します.Android Runtimeクラスのメンバー関数startRegは、関数register_を呼び出します.jni_ProcsはAndroidコアクラスのJNIメソッドを登録します.JNIメソッドを登録するには、現在のスレッドのNativeスタックに記録する必要があるJavaオブジェクトをNativeコードで参照する必要があります.しかし、この時点でDalvik仮想マシンはまだ実際に稼働していません.つまり、現在のスレッドのNativeスタックはまだ準備ができていません.この場合、JNIメソッドを登録する前に、現在のスレッドのNativeスタックに1フレーム(Frame)を手動で押し込み、JNIメソッドを登録した後、手動でフレームを弾き出す必要がある.
現在のスレッドのJNI環境は、パラメータenvが指すJNIEnvオブジェクトによって記述され、そのメンバー関数PushLocalFrameとPopLocalFrameを呼び出すことで、現在のスレッドのNativeスタックに手動でフレームを押し込み、ポップアップすることができます.なお、このフレームはローカルフレームであり、Nativeコード内のJavaオブジェクトのローカル参照を保存するためにのみ使用できます.
関数register_jni_procsは以下の通りです.
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) {
#ifndef NDEBUG
ALOGD("----------!!! %s failed to load
", array[i].mName);
#endif
return -1;
}
}
return 0;
}
前述の呼び出し手順から分かるように、パラメータarrayは、グローバル変数gRegJNIによって記述されたJNIメソッド登録関数テーブルを指し、各テーブル項目はRegJNIRecオブジェクトで記述され、各RegJNIRecオブジェクトにはメンバー変数mProcがあり、JNIメソッド登録関数を指す.これらの登録関数を順番に呼び出すことで、AndroidコアクラスのJNIメソッドを前に作成したDalvik仮想マシンに登録することができます.グローバル変数gRegJNIによって記述されたJNIメソッド登録関数テーブルを観察することで、以下に示すように、Androidコアクラスに登録されているJNIメソッドを見ることができます.
static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_debug_JNITest),
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
REG_JNI(register_android_util_FloatMath),
REG_JNI(register_android_text_format_Time),
REG_JNI(register_android_content_AssetManager),
REG_JNI(register_android_content_StringBlock),
REG_JNI(register_android_content_XmlBlock),
REG_JNI(register_android_emoji_EmojiFactory),
REG_JNI(register_android_text_AndroidCharacter),
REG_JNI(register_android_text_AndroidBidi),
REG_JNI(register_android_view_InputDevice),
REG_JNI(register_android_view_KeyCharacterMap),
REG_JNI(register_android_os_Process),
REG_JNI(register_android_os_SystemProperties),
REG_JNI(register_android_os_Binder),
REG_JNI(register_android_os_Parcel),
REG_JNI(register_android_view_DisplayEventReceiver),
REG_JNI(register_android_nio_utils),
REG_JNI(register_android_graphics_Graphics),
REG_JNI(register_android_view_GraphicBuffer),
REG_JNI(register_android_view_GLES20DisplayList),
REG_JNI(register_android_view_GLES20Canvas),
REG_JNI(register_android_view_HardwareRenderer),
REG_JNI(register_android_view_Surface),
REG_JNI(register_android_view_SurfaceControl),
REG_JNI(register_android_view_SurfaceSession),
REG_JNI(register_android_view_TextureView),
REG_JNI(register_com_google_android_gles_jni_EGLImpl),
REG_JNI(register_com_google_android_gles_jni_GLImpl),
REG_JNI(register_android_opengl_jni_EGL14),
REG_JNI(register_android_opengl_jni_EGLExt),
REG_JNI(register_android_opengl_jni_GLES10),
REG_JNI(register_android_opengl_jni_GLES10Ext),
REG_JNI(register_android_opengl_jni_GLES11),
REG_JNI(register_android_opengl_jni_GLES11Ext),
REG_JNI(register_android_opengl_jni_GLES20),
REG_JNI(register_android_opengl_jni_GLES30),
REG_JNI(register_android_graphics_Bitmap),
REG_JNI(register_android_graphics_BitmapFactory),
REG_JNI(register_android_graphics_BitmapRegionDecoder),
REG_JNI(register_android_graphics_Camera),
REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
REG_JNI(register_android_graphics_Canvas),
REG_JNI(register_android_graphics_ColorFilter),
REG_JNI(register_android_graphics_DrawFilter),
REG_JNI(register_android_graphics_Interpolator),
REG_JNI(register_android_graphics_LayerRasterizer),
REG_JNI(register_android_graphics_MaskFilter),
REG_JNI(register_android_graphics_Matrix),
REG_JNI(register_android_graphics_Movie),
REG_JNI(register_android_graphics_NinePatch),
REG_JNI(register_android_graphics_Paint),
REG_JNI(register_android_graphics_Path),
REG_JNI(register_android_graphics_PathMeasure),
REG_JNI(register_android_graphics_PathEffect),
REG_JNI(register_android_graphics_Picture),
REG_JNI(register_android_graphics_PorterDuff),
REG_JNI(register_android_graphics_Rasterizer),
REG_JNI(register_android_graphics_Region),
REG_JNI(register_android_graphics_Shader),
REG_JNI(register_android_graphics_SurfaceTexture),
REG_JNI(register_android_graphics_Typeface),
REG_JNI(register_android_graphics_Xfermode),
REG_JNI(register_android_graphics_YuvImage),
REG_JNI(register_android_graphics_pdf_PdfDocument),
REG_JNI(register_android_database_CursorWindow),
REG_JNI(register_android_database_SQLiteConnection),
REG_JNI(register_android_database_SQLiteGlobal),
REG_JNI(register_android_database_SQLiteDebug),
REG_JNI(register_android_os_Debug),
REG_JNI(register_android_os_FileObserver),
REG_JNI(register_android_os_MessageQueue),
REG_JNI(register_android_os_SELinux),
REG_JNI(register_android_os_Trace),
REG_JNI(register_android_os_UEventObserver),
REG_JNI(register_android_net_LocalSocketImpl),
REG_JNI(register_android_net_NetworkUtils),
REG_JNI(register_android_net_TrafficStats),
REG_JNI(register_android_net_ethernet_EthernetManager),
REG_JNI(register_android_net_pppoe_PppoeManager),
REG_JNI(register_android_net_wifi_WifiNative),
REG_JNI(register_android_os_MemoryFile),
REG_JNI(register_com_android_internal_os_ZygoteInit),
REG_JNI(register_android_hardware_Camera),
REG_JNI(register_android_hardware_camera2_CameraMetadata),
REG_JNI(register_android_hardware_SensorManager),
REG_JNI(register_android_hardware_SerialPort),
REG_JNI(register_android_hardware_UsbDevice),
REG_JNI(register_android_hardware_UsbDeviceConnection),
REG_JNI(register_android_hardware_UsbRequest),
REG_JNI(register_android_media_AudioRecord),
REG_JNI(register_android_media_AudioSystem),
REG_JNI(register_android_media_AudioTrack),
REG_JNI(register_android_media_JetPlayer),
REG_JNI(register_android_media_RemoteDisplay),
REG_JNI(register_android_media_ToneGenerator),
REG_JNI(register_android_opengl_classes),
REG_JNI(register_android_server_NetworkManagementSocketTagger),
REG_JNI(register_android_server_Watchdog),
REG_JNI(register_android_ddm_DdmHandleNativeHeap),
REG_JNI(register_android_backup_BackupDataInput),
REG_JNI(register_android_backup_BackupDataOutput),
REG_JNI(register_android_backup_FileBackupHelperBase),
REG_JNI(register_android_backup_BackupHelperDispatcher),
REG_JNI(register_android_app_backup_FullBackup),
REG_JNI(register_android_app_ActivityThread),
REG_JNI(register_android_app_NativeActivity),
REG_JNI(register_android_view_InputChannel),
REG_JNI(register_android_view_InputEventReceiver),
REG_JNI(register_android_view_InputEventSender),
REG_JNI(register_android_view_InputQueue),
REG_JNI(register_android_view_KeyEvent),
REG_JNI(register_android_view_MotionEvent),
REG_JNI(register_android_view_PointerIcon),
REG_JNI(register_android_view_VelocityTracker),
REG_JNI(register_android_content_res_ObbScanner),
REG_JNI(register_android_content_res_Configuration),
REG_JNI(register_android_animation_PropertyValuesHolder),
REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
REG_JNI(register_com_android_internal_net_NetworkStatsFactory),
};
6)zygote main関数の起動
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'
", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
まとめ:
Dalvik仮想マシンのZygoteプロセスでの起動プロセスを分析しました.この起動プロセスは主に以下の4つのことを完了しました.1.Dalvik仮想マシンのインスタンスを作成しました.2.JavaコアクラスとそのJNIメソッドをロードした.3.メインスレッドのJNI環境が設定されている.4.AndroidコアクラスのJNIメソッドを登録しました.
すなわち、ZygoteプロセスはAndroidシステムのためにDalvik仮想マシンインスタンスを用意し、その後、ZygoteプロセスはAndroidアプリケーションプロセスを作成する際に、自身のDalvik仮想マシンインスタンスを新たにAndroidアプリケーションプロセスを作成することにコピーし、Androidアプリケーションプロセスの開始プロセスを加速させることができる.また、JavaコアクラスとAndroidコアクラス(dexファイルにある)、およびそれらのJNIメソッド(soファイルにある)は、いずれもメモリマッピングで読み取られるため、ZygoteプロセスはAndroidアプリケーションプロセスを作成する際に、自身のDalvik仮想マシンインスタンスを新しく作成したAndroidアプリケーションプロセスにコピーできるほか、また、JavaコアクラスとAndroidコアクラス、およびJNIメソッドを新しく作成したAndroidアプリケーションプロセスと共有することで、メモリ消費を節約できます.また、ZygoteプロセスはAndroidアプリケーションプロセスの起動プロセスを加速させるために、Javaコアクラスを大量にロードし、AndroidコアクラスJNIメソッドを大量に登録する必要があるため、自分の起動速度を犠牲にしていることも見られます.Dalvik仮想マシンは、Javaコアクラスをロードするときに検証および最適化する必要があります.これらは通常、時間がかかります.また、Zygoteプロセスはinitプロセスによって開始されるため、つまりZygoteプロセスは起動時に開始されるため、Zygoteプロセスの犠牲は比較的大きい.しかし、結局、私たちが携帯電話をいじっている間に、電源を切ることはめったにありません.つまり、電源を入れることは少ないので、Zygoteプロセスの起動速度を犠牲にするのは価値があります.代わりにAndroidアプリケーションの迅速な起動です.また、AndroidシステムはJavaクラスのロード速度を速めるために、Dalvik Optimization and Verification With dexoptを参照して、Dexファイルの検証と最適化を事前に行う方法も考えられます.