【分析】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函数中相关的介绍。

时间: 2024-10-05 12:49:29

【分析】dalvik虚拟机启动过程(二)的相关文章

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

源码版本:Android-4.4.4_r2 提示:大部分分析直接注释在代码内. 相关文章: [分析]dalvik虚拟机启动过程(一) [分析]dalvik虚拟机启动过程(二) 在AndroidRuntime::start中调用AndroidRuntime::startVm函数启动了虚拟机,然后又调用了AndroidRuntime::startReg函数注册Android函数: /* * 向 VM 注册Android本地函数. * Register android native functions

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

源码版本:Android-4.4.4_r2 提示:大部分分析直接注释在代码内. 主要函数的调用层次: |AndroidRuntime::start |AndroidRuntime::startVm |JNI_CreateJavaVM |dvmCreateJNIEnv |dvmStartup |dvmThreadStartup |pthread_key_create |dvmAllocBitVector |allocThread |prepareThread |assignThreadId |dv

Android 源码分析-Dalvik 虚拟机创建过程

更多完整项目下载.未完待续.源码.图文知识后续上传github.可以点击关于我?联系我获取 一. 介绍Dalvik 1.java的运行需要JVM,同样android中使用了java语言,也需要一个VM.针对手机处理器和内存等硬件资源不足而推出来的一款VM,为android运行提供环境,叫DVM. 2.Dalvik虚拟机允许多个instance的存在.实际上android中的每一个app都是运行在自己VM实例之中(沙盒).每一个VM实例在linux中又是一个单独的进程,所以可以认为是同一个概念.运

openstack学习笔记一 虚拟机启动过程代码跟踪

本文主要通过对虚拟机创建过程的代码跟踪.观察虚拟机启动任务状态的变化,来透彻理解openstack各组件之间的作用过程. 当从horizon界面发送一个创建虚拟机请求,horizon api 将会依据前端给定的数据信息.调用novaclient 生成一个创建虚拟机的http post 请求来创建vm服务. >/usr/lib/python2.6/site-packages/horizon/api/nova.py(334)server_create() > /usr/lib/python2.6/

一步一步分析新建App启动过程

IOS温故而知新(一)  一步一步分析新建App启动过程 笔者接触IOS大概半年的时间,可以说能够开发App,但是又有很多不足的地方,因此打算写一些技术文章,能够达到温故而知新的效果,难免会有错误或者遗漏,也希望各位看官不吝指教.另外宣传一下刚建立的群“IOS菜鸟到高手之路”,312747764,欢迎希望交流分享的人加入. 1.IOS系统的简单说明IOS与Mac OS X以及安卓一样都属于类UINX系统,然而熟悉类UNIX系统的人可能会失望的是,个人开发的App会进入类似chroot的状态,被操

Android 源码分析(十) Dalvik 虚拟机创建过程

一. 介绍Dalvik 1.java的运行需要JVM(后面有大量篇幅介绍),同样android中使用了java语言,也需要一个VM.针对手机处理器和内存等硬件资源不足而推出来的一款VM,为android运行提供环境,叫DVM. 2.Dalvik虚拟机允许多个instance的存在.实际上android中的每一个app都是运行在自己VM实例之中(沙盒).每一个VM实例在linux中又是一个单独的进程,所以可以认为是同一个概念.运行在自己的DVM进程之中,不同的app不会相互干扰,且不会因为一个DV

linux内核学习之三 跟踪分析内核的启动过程

一   前期准备工作       1 搭建环境 1.1下载内核源代码并编译内核 创建目录,并进入该目录: 下载源码: 解压缩,并进入该目录:xz -d linux-3.18.6.tar.xz tar -xvf linux-3.18.6 cd  linux-3.18.6 选定x86架构的相关文件编译: 编译: 1.2 制作根文件系统 在工作目录下新建一个文件夹: mkdir rootfs 下载老师提供的资料:git clone https://github.com/mengning/menu.gi

wxWidgets源码分析(1) - App启动过程

目录 APP启动过程 wxApp入口定义 wxApp实例化准备 wxApp的实例化 wxApp运行 总结 APP启动过程 本文主要介绍wxWidgets应用程序的启动过程,从app.cpp入手. wxApp入口定义 wxApp通过IMPLEMENT_APP宏注册App类,这个宏同时定义了入口,实现在wx/app.h文件中. // wx/app.h 文件中定义 #define IMPLEMENT_APP(app) wxIMPLEMENT_APP(app); // 可以忽略 wxIMPLEMENT_

Activity启动流程源码分析之Launcher启动(二)

1.前述 在前一篇文章中我们简要的介绍Activity的启动流程Activity启动流程源码分析之入门(一),当时只是简单的分析了一下流程,而且在上一篇博客中我们也说了Activity的两种启动方式,现在我们就来分析其中的第一种方式--Launcher启动,这种启动方式的特点是会创建一个新的进程来加载相应的Activity(基于Android5.1源码). 2.Activity启动流程时序图 好啦,接下来我们先看一下Launcher启动Activity的时序图: 好啦,接下来我们将上述时序图用代