<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } -->

要分析Dalvik虚拟机的代码,到底从那里开始比较好呢?从事软件开发人员都知道,每个程序都有生命周期,都有出生点,也就是程序的进入位置。像C语言里控制台程序是使用main函数作为入口点的,java程序也是使用main函数作为入口点。其实Dalvik虚拟机作为应用程序启动时,也是一样的从main函数开始。从Dalvik虚拟机源码目录dalvik/dalvikvm/Main.c文件,就可以看到入口函数,如下:

/*
* Parse arguments. Most of it just gets passed through to the VM. The
* JNI spec defines a handful of standard arguments.
*/
int main(int argc, char* const argv[])
{
JavaVM* vm = NULL;
JNIEnv* env = NULL;
JavaVMInitArgs initArgs;
JavaVMOption* options = NULL;
char* slashClass = NULL;
int optionCount, curOpt, i, argIdx;
int needExtra = JNI_FALSE;
int result = 1;
 
setvbuf(stdout, NULL, _IONBF, 0);
 
/* ignore argv[0] */
argv++;
argc--;
...

因此,当你编译Dalvik虚拟机作为linux下x86应用程序来运行时,就需要注意了,这里是它的入口。

 

也许你会问,为什么需要Dalvik虚拟机作为linux下x86应用程序运行,它不是在手机平台ARM里运行的吗?这个问题问得好,问到点子上了。其实Dalvik虚拟机作为linux下x86应用程序运行,是用来开发和调试使用的,当需要作为手机平台使用时,它不是从Main函数作为入口点。嵌入式的开发人员都知道,在一个资源有限平台里开发和调试,都是一件不容易的事情,需要花费很多时间,那么有没有更好的方法来开发和调试呢?答案是有的。就是利用目前x86的平台,这样可以使用x86大量的工具和资源,大大地提高开发效率,更容易调试系统的功能。在Dalvik虚拟机进行开发新功能时,可以先在x86的平台上运行和调试通过,然后再编译在ARM平台运行。我曾经修改这个虚拟机的代码后,再在系统里编译和运行,发觉刚刚编译整个平台的时间,就非常多,在4核的CPU、4G内存的linux系统下,再利用make -j4等多核编译技术,也需要好几分钟,这样每修改几行代码,添加一些细小的功能,就需编译测试一次,付出的时间成本太多了,显然需要另找出路,那么就是使用x86平台方式生成应用程序来测试。还有另外一个问题,就是单步调试的功能,在ARM平台也是比较麻烦的事情,程序有时会跑飞等等。

 

下面再来分析另外一个入口点,就是android的dalvik虚拟机在ARM平台运行的入口点。它显然不会通过Main函数来加载的,而从初始化进程加载的服务Zygote开始的。从Zygote服务代码,可以看到下面代码:

runtime.start("com.android.internal.os.ZygoteInit",
startSystemServer);

这里根据命令行参数来创建Dalvik虚拟机,并从 com.android.internal.os.ZygoteInit开始运行,然后启动所有Dalvik虚拟服务。 这这里的runtime对象,就是类AppRuntime,而类AppRuntime继承类AndroidRuntime,那么类AndroidRuntime是何物呢?类AndroidRuntime是android平台核心框架里的代码,它是实现JNI的基石,也就是说所有Dalvik虚拟机里的应用程序要访问底层系统的功能,都是通过JNI来实现的。可以从下面的文件找到这个类:

frameworks/base/core/jni/AndroidRuntime.cpp

在这个文件里,可以看到上面调用函数 start的实现,如下:

/*
* 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".
*/
void AndroidRuntime::start(const char* className, const bool startSystemServer)
{
LOGD("/n>>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<</n");
 
char* slashClassName = NULL;
char* cp;
JNIEnv* env;
 
blockSigpipe();
 
/*
* 'startSystemServer == true' means runtime is obslete and not run from
* init.rc anymore, so we print out the boot start event here.
*/
if (startSystemServer) {
/* 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.");
goto bail;
}
setenv("ANDROID_ROOT", rootDir, 1);
}
 
//const char* kernelHack = getenv("LD_ASSUME_KERNEL");
//LOGD("Found LD_ASSUME_KERNEL='%s'/n", kernelHack);
 
/* start the virtual machine */
if (startVm(&mJavaVM, &env) != 0)
goto bail;
 
/*
* Register android functions.
*/
if (startReg(env) < 0) {
LOGE("Unable to register all android natives/n");
goto bail;
}

 

从上面的代码片段里,可以看到调用函数startVm,这个函数就是创建Dalvik虚拟机,继续查看函数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.
*/
LOGD("JNI_CreateJavaVM Start.../n");
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
LOGE("JNI_CreateJavaVM failed/n");
goto bail;
}
LOGD("JNI_CreateJavaVM End/n");
 
result = 0;

 

在这段代码里,主要任务就是初始化Dalvik虚拟机,通过调用 JNI_CreateJavaVM函数来实现的,而 JNI_CreateJavaVM函数的实现,就是在文件Dalvik/vm/Jni.c里。

 

到这里,就已经把另一个入口点分析完成了,通过上面的学习可以知道不同的入口,是来源于现实开发的需要,通过这些大型代码工程,可以学习到很多开发技巧,提高效率的方法。

<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } A:link { so-language: zxx } --> 
//QQ: 9073204 EMAIL:9073204@qq.com 
//蔡军生  2011-06-19