源碼版本: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
函數中相關的介紹。