【分析】dalvik虚拟机启动过程(二)

时间:2022-05-16 09:28:59

源码版本:Android-4.4.4_r2

提示:大部分分析直接注释在代码内。


接着上一篇【分析】dalvik虚拟机启动过程(一)

JNI_CreateJavaVM函数调用dvmCreateJNIEnv创建JNIEnv后,接着又调用了dvmStartup函数初始化VM:

/*
* VM 初始化。
* VM initialization. Pass in any options provided on the command line.
* Do not pass in the class name or the options for the class.
*
* 如果成功的话,则返回空字符串。
* Returns 0 on success.
*
* argc:参数个数。
* argv:参数数组。
*/
std::string dvmStartup(int argc, const char* const argv[],
bool ignoreUnrecognized, JNIEnv* pEnv)
{
ScopedShutdown scopedShutdown;

assert(gDvm.initializing);

ALOGV("VM init args (%d):", argc);
for (int i = 0; i < argc; i++) {
ALOGV(" %d: '%s'", i, argv[i]);
}
// 设置默认值。
setCommandLineDefaults();

// 处理选项标志(如果有的话)。
/*
* Process the option flags (if any).
*/
int cc = processOptions(argc, argv, ignoreUnrecognized);
if (cc != 0) {
if (cc < 0) {
dvmFprintf(stderr, "\n");
usage("dalvikvm");
}
return "syntax error";
}

......

// 验证系统页大小。
/* verify system page size */
if (sysconf(_SC_PAGESIZE) != SYSTEM_PAGE_SIZE) {
return StringPrintf("expected page size %d, got %d",
SYSTEM_PAGE_SIZE, (int) sysconf(_SC_PAGESIZE));
}

// 验证用于mterp解释器的常量。
/* mterp setup */
ALOGV("Using executionMode %d", gDvm.executionMode);
dvmCheckAsmConstants();

// 初始化组件。
/*
* Initialize components.
*/
dvmQuasiAtomicsStartup();
if (!dvmAllocTrackerStartup()) {
return "dvmAllocTrackerStartup failed";
}
if (!dvmGcStartup()) {
return "dvmGcStartup failed";
}
// 初始化线程。
if (!dvmThreadStartup()) {
return "dvmThreadStartup failed";
}

......
}

dvmSartup函数中调用了 dvmThreadStartup 函数初始化了线程,这个函数在 dalvik/vm/Thread.cpp 文件中:

/*
* 初始化线程列表和主线程的环境。
* 我们需要设置一些东西,当我们开始加载类时dvmThreadSelf()将会工作。
*
* Initialize thread list and main thread's environment. We need to set
* up some basic stuff so that dvmThreadSelf() will work when we start
* loading classes (e.g. to check for exceptions).
*/
bool dvmThreadStartup()
{
Thread* thread;

// 分配一个线程局部存储。
/* allocate a TLS slot */
if (pthread_key_create(&gDvm.pthreadKeySelf, threadExitCheck) != 0) {
ALOGE("ERROR: pthread_key_create failed");
return false;
}

/* test our pthread lib */
if (pthread_getspecific(gDvm.pthreadKeySelf) != NULL)
ALOGW("WARNING: newly-created pthread TLS slot is not NULL");

// 准备与线程相关的锁和条件。
/* prep thread-related locks and conditions */
dvmInitMutex(&gDvm.threadListLock);
pthread_cond_init(&gDvm.threadStartCond, NULL);
pthread_cond_init(&gDvm.vmExitCond, NULL);
dvmInitMutex(&gDvm._threadSuspendLock);
dvmInitMutex(&gDvm.threadSuspendCountLock);
pthread_cond_init(&gDvm.threadSuspendCountCond, NULL);

// 专用于监听Thread.sleep()。
//
/*
* Dedicated monitor for Thread.sleep().
* TODO: change this to an Object* so we don't have to expose this
* call, and we interact better with JDWP monitor calls. Requires
* deferring the object creation to much later (e.g. final "main"
* thread prep) or until first use.
*/
gDvm.threadSleepMon = dvmCreateMonitor(NULL);

/* 创建线程Id映射。
* 返回的BitVector结构可以保存的数据为 (kMaxThreadId + 31) >> 5 个位,
* 详见下面的dvmAllocBitVector函数说明。
*
* gDvm.threadIdMap中保存所有线程的线程Id。
* 如果线程Id为1,那么第1位置1,如果线程Id为而,则第2位置1,以此类推。
* 位索引从0开始。保留0作为无效的线程Id。
* 这里所说的线程Id,与pthread_self()的返回值不是一回事。
*
* gettid()是内核中的线程的ID。(linux使用进程模拟线程,gettid 函数返回实际的进程ID,这么展开话就长了……)
* pthread_self()获取的是POSIX thread ID。
* 所以我认为,gDvm.threadIdMap内的线程Id,代表的是Android虚拟机内的线程Id。
*
* #define kMaxThreadId ((1 << 16) - 1),即65535
* kMaxThreadId表示线程id的最大个数。
*/
gDvm.threadIdMap = dvmAllocBitVector(kMaxThreadId, false);

// 动态分配和初始化一个Thread结构。
thread = allocThread(gDvm.mainThreadStackSize);
if (thread == NULL)
return false;

// 线程状态:正在运行。
/* switch mode for when we run initializers */
thread->status = THREAD_RUNNING;

// 完成一个Thread结构的初始化。
/*
* We need to assign the threadId early so we can lock/notify
* object monitors. We'll set the "threadObj" field later.
*/
prepareThread(thread);
gDvm.threadList = thread;

#ifdef COUNT_PRECISE_METHODS
gDvm.preciseMethods = dvmPointerSetAlloc(200);
#endif

return true;
}

dvmAllocBitVector函数:

/*
* Allocate a bit vector with enough space to hold at least the specified
* number of bits.
*
* expandable:当BitVector中的位使用完以后,是否扩展。true:扩展。false:不扩展。
*/
BitVector* dvmAllocBitVector(unsigned int startBits, bool expandable)
{
BitVector* bv;
unsigned int count;

assert(sizeof(bv->storage[0]) == 4); /* assuming 32-bit units */

bv = (BitVector*) malloc(sizeof(BitVector));

// count代表数组元素个数。
count = (startBits + 31) >> 5;

bv->storageSize = count;
bv->expandable = expandable;
bv->storage = (u4*) calloc(count, sizeof(u4));
return bv;
}

allocThread函数动态分配和初始化一个Thread,这个函数在 dalvik/vm/Thread.cpp 文件中:

/*
* 分配和初始化一个线程结构。
* Alloc and initialize a Thread struct.
*
* 不要创建任何对象,......
* Does not create any objects, just stuff on the system (malloc) heap.
*/
static Thread* allocThread(int interpStackSize)
{
Thread* thread;
u1* stackBottom;

// 从堆中分配内存给Thread结构。
thread = (Thread*) calloc(1, sizeof(Thread));
if (thread == NULL)
return NULL;

/* Check sizes and alignment */
assert((((uintptr_t)&thread->interpBreak.all) & 0x7) == 0);
assert(sizeof(thread->interpBreak) == sizeof(thread->interpBreak.all));

......

return thread;
}

prepareThread函数完成一个Thread结构的初始化,这个函数在 dalvik/vm/Thread.cpp 文件中:

/*
* 完成一个Thread结构的初始化。
* Finish initialization of a Thread struct.
*
* 必须同时在新的线程中执行调用,但是在线程被添加到线程列表之前。
* This must be called while executing in the new thread, but before the
* thread is added to the thread list.
*
* 注意:threadListLock必须由调用者维护(需要assignThreadId())。
* NOTE: The threadListLock must be held by the caller (needed for
* assignThreadId()).
*/
static bool prepareThread(Thread* thread)
{
assignThreadId(thread); // 分配一个线程Id。其实就是为thread->threadId赋值。
thread->handle = pthread_self();
thread->systemTid = dvmGetSysThreadId();

//ALOGI("SYSTEM TID IS %d (pid is %d)", (int) thread->systemTid,
// (int) getpid());
// 将thread设置到线程局部存储。
// 如果我们通过 dvmAttachCurrentThread 调用,self值已经正确的成为"thread"。
/*
* If we were called by dvmAttachCurrentThread, the self value is
* already correctly established as "thread".
*/
setThreadSelf(thread);

ALOGV("threadid=%d: interp stack at %p",
thread->threadId, thread->interpStackStart - thread->interpStackSize);

......
}

assignThreadId函数为Thread结构分配了一个线程Id,这个线程Id与gettid和pthread_self函数返回的线程Id不同,我认为这个线程Id代表这个线程在dalvik虚拟机内的线程Id,这个函数在 dalvik/vm/Thread.cpp 文件中:

/*
* 分配一个线程ID。这需要...
* Assign the threadId. This needs to be a small integer so that our
* "thin" locks fit in a small number of bits.
*
* 我们保留零用作无效的ID。
* We reserve zero for use as an invalid ID.
*
* This must be called with threadListLock held.
*/
static void assignThreadId(Thread* thread)
{
// 在threadIdMap中为线程Id分配一位。返回的(num+1)代表线程Id。
/*
* Find a small unique integer. threadIdMap is a vector of
* kMaxThreadId bits; dvmAllocBit() returns the index of a
* bit, meaning that it will always be < kMaxThreadId.
*/
int num = dvmAllocBit(gDvm.threadIdMap);
if (num < 0) {
ALOGE("Ran out of thread IDs");
dvmAbort(); // TODO: make this a non-fatal error result
}

thread->threadId = num + 1;

assert(thread->threadId != 0);
}

dvmAllocBit函数是实际分配线程Id的函数,这个函数在 dalvik/vm/BitVector.cpp 文件中:

/*
* 分配bitmap中第一个可用的位。
* "Allocate" the first-available bit in the bitmap.
*
* 这是不同步的。调用者被期望持有某种锁,以防止多个线程在dvmAllocBit/ dvmFreeBit同时执行。
* This is not synchronized. The caller is expected to hold some sort of
* lock that prevents multiple threads from executing simultaneously in
* dvmAllocBit/dvmFreeBit.
*/
int dvmAllocBit(BitVector* pBits)
{
unsigned int word, bit;

retry:
for (word = 0; word < pBits->storageSize; word++) {
// 0xffffffff代表32位均置位为1。
if (pBits->storage[word] != 0xffffffff) {
/*
* 在word中有一个位还未分配。返回找到的第一个未分配的位。
* There are unallocated bits in this word. Return the first.
*/
bit = ffs(~(pBits->storage[word])) -1;
assert(bit < 32);
pBits->storage[word] |= 1 << bit; // 对未分配的位置1。
// (word << 5) => word * 2^5 => word * 32。
return (word << 5) | bit; // 返回第几位被置位。
}
}

// 如果位都用完了,那么进行下面的判断来决定是否扩展。
/*
* Ran out of space, allocate more if we're allowed to.
*/
if (!pBits->expandable)
return -1;

pBits->storage = (u4*)realloc(pBits->storage,
(pBits->storageSize + kBitVectorGrowth) * sizeof(u4));
memset(&pBits->storage[pBits->storageSize], 0x00,
kBitVectorGrowth * sizeof(u4));
pBits->storageSize += kBitVectorGrowth;
goto retry;
}

上面函数中参数 pBits 其实是 gDvm.threadIdMap ,关于gDvm.threadIdMap可以参考上面 dvmThreadStartup 函数中相关的介绍。