1. 程式人生 > >android虛擬機器詳解

android虛擬機器詳解

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server   @system/core/rootdir/init.rc
/system/bin/app_process
|-->main():                                      @frameworks/base/cmds/app_process/app_main.cpp
虛擬機器啟動入口函式
    if (strcmp(arg, "--zygote") == 0) zygote = true;
    if (strcmp(arg, "--start-system-server") == 0) startSystemServer = true;
    if (zygote) { runtime.start("com.android.internal.os.ZygoteInit",startSystemServer ? "start-system-server" : "");
    |--> AndroidRuntime::start(const char* className, const char* options) 配置環境(ANDROID_ROOT)目錄變數等   @frameworks/base/core/jni/AndroidRuntime.cpp
        |--> AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
        |    |--> JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)                                @dalvik/vm/Jni.cpp
        |        |--> dvmStartup(int argc, const char* const argv[],bool ignoreUnrecognized, JNIEnv* pEnv)    @dalvik/vm/Init.cpp
        |            |-->初始化組建
        |            |--> dvmCheckAsmConstants() 明確mterp直譯器要用到的一些變數正確
        |            |--> dvmAllocTrackerStartup() 函式初始化跟蹤顯示系統,跟蹤系統主要用生成除錯系統的資料包
        |            |--> dvmGcStartup()    初始化垃圾回收器//初始化heap
        |            |--> dvmThreadStartup()    初始化執行緒列表和主執行緒環境引數
        |            |--> dvmInlineNativeStartup()    分配內部操作方法的表格記憶體
        |            |--> dvmRegisterMapStartup() 分配指令暫存器狀態的記憶體
        |            |--> dvmInstanceofStartup()    分配虛擬機器使用的快取
        |            |--> dvmClassStartup()    初始化虛擬機器最基本用的JAVA庫
        |            |--> dvmFindRequiredClassesAndMembers()類查詢相關的初始化
        |            |--> dvmStringInternStartup()    初始化虛擬機器直譯器使用的字串雜湊表
        |            |--> dvmNativeStartup()    初始化本地方法庫的表
        |            |--> dvmInternalNativeStartup()    初始化內部本地方法,建立雜湊表,方便快速查詢到
        |            |--> dvmJniStartup()    初始化JNI呼叫表,以便快速找到本地方法呼叫的入口
        |            |--> dvmProfilingStartup()
        |            |--> dvmCreateInlineSubsTable()建立一個方法表代替inline方法表
        |            |--> dvmValidateBoxClasses()    始化JAVA基本型別庫
        |            |--> dvmPrepMainForJni(pEnv)    準備主執行緒裡的解釋棧可以呼叫JNI的方法
        |            |--> dvmInitClass(gDvm.classJavaLangClass)初始化java.lang.Class
        |            |--> registerSystemNatives(pEnv)註冊JAVA庫裡的JNI方法
        |            |--> dvmCreateStockExceptions()分配異常出錯的記憶體
        |            |--> dvmPrepMainThread()直譯器主執行緒的初始化
        |            |--> dvmDebuggerStartup()偵錯程式初始化
        |            |--> dvmGcStartupClasses()
        |            |--> dvmInitAfterZygote()  此函式執行完虛擬機器程序建立就完成了.
        |                |--> dvmGcStartupAfterZygote()    進行一次GC
        |                |--> dvmSignalCatcherStartup()  啟動一個Linux訊號收集執行緒,主要是用來捕捉SIGQUIT訊號,以便可以在程序退出前將各個執行緒的堆疊DUMP出來
        |                |--> dvmStdioConverterStartup() 啟動一個標準輸出重定向執行緒,該執行緒負責將當前程序的標準輸出(stdout和stderr)重定向到日誌輸出系統中去
        |                |--> dvmInitJDWP()    啟動一個JDWP執行緒,以便我們可以用DDMS工具來除錯程序中的Dalvik虛擬機器
        |                |--> dvmCompilerStartup()    啟動JIT,前提是當前使用的Dalvik虛擬機器在編譯時支援JIT,並且該Dalvik虛擬機器在啟動時指定了-Xint:jit選項
        |                    |--> dvmCreateInternalThread(&gDvmJit.compilerHandle, "Compiler",compilerThreadStart, NULL);
        |                        |--> *compilerThreadStart(void *arg)    啟動執行緒初始化dalvik的編譯器,在虛擬機器支援JIT的前提下.
        |                            |--> compilerThreadStartup(void)在虛擬機器執行過程中一直生存的執行緒,while迴圈判斷是否有程式碼需要編譯,如果有,則呼叫dvmCompilerDoWork()對其進行編譯
    |--> startReg(env) //註冊android native函式
    |--> jmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");
    |--> env->CallStaticVoidMethod(startClass, startMeth, strArray);    step1: JNIEnv.CallStaticVoidMethod setp2: JNINativeInterface.CallStaticVoidMethodV
        |--> functions->CallStaticVoidMethodV(this, clazz, methodID, args);                   
        |--> dvmInterpret(Thread* , const Method*, JValue* ) (Main interpreter loop entry point.將上一個啟用狀態儲存到當前直譯器,
                                                                    初始化工作狀態(比如,執行的method賦值),選擇直譯器執行模式)

        if  |--> dvmMterpStd :此函式為指標函式函式名,為直譯器彙編實現的入口
        else|--> dvmInterpretPortable :此函式為可移植C的直譯器實現入口
#                typedef void (*Interpreter)(Thread*);
#                Interpreter stdInterp;
#                if (gDvm.executionMode == kExecutionModeInterpFast)
#                    stdInterp = dvmMterpStd;
#                #if defined(WITH_JIT)
#                    else if (gDvm.executionMode == kExecutionModeJit)
#                        stdInterp = dvmMterpStd;
#                #endif
#                    else
#                    stdInterp = dvmInterpretPortable;
#                    // Call the interpreter
#                    (*stdInterp)(self);
分支到dvmInterpretPortable,比較容易看懂:

dvmInterpretPortable(Thread* self)            @vm/mterp/out/InterpC-portable.cpp
void dvmInterpretPortable(Thread* self)
{
#if defined(EASY_GDB)
    StackSaveArea* debugSaveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame);
#endif
    DvmDex* methodClassDex;     // curMethod->clazz->pDvmDex
    JValue retval;

    /* core state */
    const Method* curMethod;    // method we're interpreting
    const u2* pc;               // program counter
    u4* fp;                     // frame pointer
    u2 inst;                    // current instruction
    /* instruction decoding */
    u4 ref;                     // 16 or 32-bit quantity fetched directly
    u2 vsrc1, vsrc2, vdst;      // usually used for register indexes
    /* method call setup */
    const Method* methodToCall;
    bool methodCallRange;
    bool jumboFormat;


    /* static computed goto table */
    DEFINE_GOTO_TABLE(handlerTable);

    /* copy state in */
    curMethod = self->interpSave.method;
    pc = self->interpSave.pc;
    fp = self->interpSave.curFrame;
    retval = self->interpSave.retval;   /* only need for kInterpEntryReturn? */

    methodClassDex = curMethod->clazz->pDvmDex;
    LOGVV("threadid=%d: %s.%s pc=%#x fp=%p",
        self->threadId, curMethod->clazz->descriptor, curMethod->name,
        pc - curMethod->insns, fp);

    /*
     * Handle any ongoing profiling and prep for debugging.
     */
    if (self->interpBreak.ctl.subMode != 0) {
        TRACE_METHOD_ENTER(self, curMethod);
        self->debugIsMethodEntry = true;   // Always true on startup
    }
    /*
     * DEBUG: scramble this to ensure we're not relying on it.
     */
    methodToCall = (const Method*) -1;

#if 0
    if (self->debugIsMethodEntry) {
        ILOGD("|-- Now interpreting %s.%s", curMethod->clazz->descriptor,
                curMethod->name);
        DUMP_REGS(curMethod, self->interpSave.curFrame, false);
    }
#endif

    FINISH(0);                  /* fetch and execute first instruction */

/*--- start of opcodes ---*/

/* File: c/OP_NOP.cpp */
HANDLE_OPCODE(OP_NOP)
    FINISH(1);
OP_END

/* File: c/OP_MOVE.cpp */
HANDLE_OPCODE(OP_MOVE /*vA, vB*/)
    vdst = INST_A(inst);
    vsrc1 = INST_B(inst);
    ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
        (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
        kSpacing, vdst, GET_REGISTER(vsrc1));
    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
    FINISH(1);
OP_END

 

首先直譯器先載入dex檔案,從中取出函式,函式就如x86的函式地址一樣,虛擬機器的opcode如彙編指令。opcode比彙編功能多一些,高階一些,所以個別opcode不想彙編那麼簡潔。須要呼叫函式來實現。
如函式內調轉另一個函式,就需要使用函式來實現,需要傳引數。








在看看彙編:dvmMterpStd:                
void dvmMterpStd(Thread* self)
{
    /* configure mterp items */
    self->interpSave.methodClassDex = self->interpSave.method->clazz->pDvmDex;

    IF_LOGVV() {
        char* desc = dexProtoCopyMethodDescriptor(
                         &self->interpSave.method->prototype);
        LOGVV("mterp threadid=%d : %s.%s %s",
            dvmThreadSelf()->threadId,
            self->interpSave.method->clazz->descriptor,
            self->interpSave.method->name,
            desc);
        free(desc);
    }
    //LOGI("self is %p, pc=%p, fp=%p", self, self->interpSave.pc,
    //      self->interpSave.curFrame);
    //LOGI("first instruction is 0x%04x", self->interpSave.pc[0]);

    /*
     * Handle any ongoing profiling and prep for debugging
     */
    if (self->interpBreak.ctl.subMode != 0) {
        TRACE_METHOD_ENTER(self, self->interpSave.method);
        self->debugIsMethodEntry = true;   // Always true on startup
    }

    dvmMterpStdRun(self);

#ifdef LOG_INSTR
    LOGD("|-- Leaving interpreter loop");
#endif
}

@InterpAsm-armv5te.S

OP如何連線執行?

.L_OP_MOVE: /* 0x01 */
/* File: armv5te/OP_MOVE.S */
    /* for move, move-object, long-to-int */
    /* op vA, vB */
    mov     r1, rINST, lsr #12          @ r1<- B from 15:12
    mov     r0, rINST, lsr #8           @ r0<- A from 11:8
    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
    GET_VREG(r2, r1)                    @ r2<- fp[B]
    and     r0, r0, #15
    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
    SET_VREG(r2, r0)                    @ fp[A]<- r2

    GOTO_OPCODE(ip)                     @ execute next instruction

抓取第一個Dalvik OpCode指令並執行,同時在每個指令執行結束後,再透過FETCH_ADVANCE_INST與GET_INST_OPCODE抓取下一個Dalvik OpCode指令,最後再透過GOTO_OPCODE執行該指令集的實作,如此持續運作下去,藉此得到比用C版本Busy Loop更高的執行效率.