天天看點

【分析】dalvik虛拟機啟動過程(二)

源碼版本: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

函數中相關的介紹。

繼續閱讀