CoreCLR系列随笔 之ClrJit项目之alloc.cpp文件分析(1)

  首先声明,自己对CLR了解得不多,只是个人爱好,可能有错误,请指出,文件源码如下(可能不是最新的)

//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
/*****************************************************************************/


#include "jitpch.h"
#ifdef _MSC_VER
#pragma hdrstop
#endif
/*****************************************************************************/

/*****************************************************************************/
void                allocatorCodeSizeBeg(){}
/*****************************************************************************/
#ifdef  DEBUG
/*****************************************************************************/

void    __cdecl     debugStop(const char *why, ...)
{
    va_list     args;

    va_start(args, why);

    printf("NOTIFICATION: ");
    if  (why)
        vprintf(why, args);
    else
        printf("debugStop(0)");

    printf("\n");

    va_end(args);

    BreakIfDebuggerPresent();
}

/*****************************************************************************/

/* 
 * Does this constant need to be bigger?
 */
static  size_t    blockStop    = 99999999;

/*****************************************************************************/
#endif // DEBUG
/*****************************************************************************/

size_t THE_ALLOCATOR_BASE_SIZE  = 0;

bool   norls_allocator::nraInit(IEEMemoryManager* pMemoryManager, size_t pageSize, int preAlloc)
{
    bool    result = false;

    nraMemoryManager = pMemoryManager;

    nraPageList  =
    nraPageLast  = 0;

    nraFreeNext  =
    nraFreeLast  = 0;

    assert(THE_ALLOCATOR_BASE_SIZE != 0);

    nraPageSize  = pageSize ? pageSize : THE_ALLOCATOR_BASE_SIZE;

#ifdef DEBUG
    static ConfigDWORD fShouldInjectFault;
    nraShouldInjectFault = fShouldInjectFault.val(CLRConfig::INTERNAL_InjectFault) != 0;
#endif    

    if  (preAlloc)
    {
        /* Grab the initial page(s) */

        setErrorTrap(NULL, norls_allocator *, pThis, this)  // ERROR TRAP: Start normal block
        {
            pThis->nraAllocNewPage(0);
        }
        impJitErrorTrap()  // ERROR TRAP: The following block handles errors
        {
            result = true;
        }
        endErrorTrap()  // ERROR TRAP: End
    }

    return  result;
}

/*---------------------------------------------------------------------------*/

void    *   norls_allocator::nraAllocNewPage(size_t sz)
{
    norls_pagdesc * newPage;
    size_t          sizPage;

    size_t          realSize = sz + sizeof(norls_pagdesc);
    if (realSize < sz) 
        NOMEM();   // Integer overflow

    /* Do we have a page that's now full? */

    if  (nraPageLast)
    {
        /* Undo the "+=" done in nraAlloc() */

        nraFreeNext -= sz;

        /* Save the actual used size of the page */

        nraPageLast->nrpUsedSize = nraFreeNext - nraPageLast->nrpContents;
    }

    /* Make sure we grab enough to satisfy the allocation request */

    sizPage = nraPageSize;

    if  (sizPage < realSize)
    {
        /* The allocation doesn't fit in a default-sized page */

#ifdef  DEBUG
//      if  (nraPageLast) printf("NOTE: wasted %u bytes in last page\n", nraPageLast->nrpPageSize - nraPageLast->nrpUsedSize);
#endif

        sizPage = realSize;
    }

    /* Round to the nearest multiple of OS page size */

    if (!nraDirectAlloc())
    {
        sizPage +=  (DEFAULT_PAGE_SIZE - 1);
        sizPage &= ~(DEFAULT_PAGE_SIZE - 1);
    }

    /* Allocate the new page */

    newPage = (norls_pagdesc *)nraVirtualAlloc(0, sizPage, MEM_COMMIT, PAGE_READWRITE);
    if  (!newPage)
        NOMEM();

#ifdef DEBUG
    newPage->nrpSelfPtr = newPage;
#endif

    /* Append the new page to the end of the list */

    newPage->nrpNextPage = 0;
    newPage->nrpPageSize = sizPage;
    newPage->nrpPrevPage = nraPageLast;
    newPage->nrpUsedSize = 0;  // nrpUsedSize is meaningless until a new page is allocated.
                               // Instead of letting it contain garbage (so to confuse us),
                               // set it to zero.

    if  (nraPageLast)
        nraPageLast->nrpNextPage = newPage;
    else
        nraPageList              = newPage;
    nraPageLast = newPage;

    /* Set up the 'next' and 'last' pointers */

    nraFreeNext = newPage->nrpContents + sz;
    nraFreeLast = newPage->nrpPageSize + (BYTE *)newPage;

    assert(nraFreeNext <= nraFreeLast);

    return  newPage->nrpContents;
}

// This method walks the nraPageList forward and release the pages.
// Be careful no other thread is doing nraToss at the same time.
// Otherwise, the page specified by temp could be double-freed (VSW 600919).

void        norls_allocator::nraFree(void)
{
    /* Free all of the allocated pages */

    while   (nraPageList)
    {
        norls_pagdesc * temp;

        temp = nraPageList;
               nraPageList = temp->nrpNextPage;

        nraVirtualFree(temp, 0, MEM_RELEASE);
    }
}

// This method walks the nraPageList backward and release the pages.
// Be careful no other thread is doing nraFree as the same time.
// Otherwise, the page specified by temp could be double-freed (VSW 600919).
void        norls_allocator::nraToss(nraMarkDsc &mark)
{
    void    *   last = mark.nmPage;

    if  (!last)
    {
        if  (!nraPageList)
            return;

        nraFreeNext  = nraPageList->nrpContents;
        nraFreeLast  = nraPageList->nrpPageSize + (BYTE *)nraPageList;

        return;
    }

    /* Free up all the new pages we've added at the end of the list */

    while (nraPageLast != last)
    {
        norls_pagdesc * temp;

        /* Remove the last page from the end of the list */

        temp = nraPageLast;
               nraPageLast = temp->nrpPrevPage;

        /* The new last page has no 'next' page */

        nraPageLast->nrpNextPage = 0;

        nraVirtualFree(temp, 0, MEM_RELEASE);
    }

    nraFreeNext = mark.nmNext;
    nraFreeLast = mark.nmLast;
}

/*****************************************************************************/
#ifdef DEBUG
/*****************************************************************************/
void    *           norls_allocator::nraAlloc(size_t sz)
{
    void    *   block;

    assert(sz != 0 && (sz & (sizeof(int) - 1)) == 0);
#ifdef _WIN64
    //Ensure that we always allocate in pointer sized increments.
    /* TODO-Cleanup:
     * This is wasteful.  We should add alignment requirements to the allocations so we don't waste space in
     * the heap.
     */
    sz = (unsigned)roundUp(sz, sizeof(size_t));
#endif

#ifdef DEBUG
    if (nraShouldInjectFault)
    {
        // Force the underlying memory allocator (either the OS or the CLR hoster) 
        // to allocate the memory. Any fault injection will kick in.
        void * p = DbgNew(1); 
        if (p) 
        {
            DbgDelete(p);
        }
        else 
        {
            NOMEM();  // Throw!
        }
    }
#endif    

    block = nraFreeNext;
            nraFreeNext += sz;

    if  ((size_t)block == blockStop) debugStop("Block at %08X allocated", block);

    if  (nraFreeNext > nraFreeLast)
        block = nraAllocNewPage(sz);

#ifdef DEBUG
    memset(block, UninitializedWord<char>(), sz);
#endif

    return  block;
}

/*****************************************************************************/
#endif
/*****************************************************************************/

size_t              norls_allocator::nraTotalSizeAlloc()
{
    norls_pagdesc * page;
    size_t          size = 0;

    for (page = nraPageList; page; page = page->nrpNextPage)
        size += page->nrpPageSize;

    return  size;
}

size_t              norls_allocator::nraTotalSizeUsed()
{
    norls_pagdesc * page;
    size_t          size = 0;

    if  (nraPageLast)
        nraPageLast->nrpUsedSize = nraFreeNext - nraPageLast->nrpContents;

    for (page = nraPageList; page; page = page->nrpNextPage)
        size += page->nrpUsedSize;

    return  size;
}

/*****************************************************************************
 * We try to use this allocator instance as much as possible. It will always
 * keep a page handy so small methods won't have to call VirtualAlloc()
 * But we may not be able to use it if another thread/reentrant call
 * is already using it.
 */

static norls_allocator *nraTheAllocator;
static nraMarkDsc       nraTheAllocatorMark;
static LONG             nraTheAllocatorIsInUse = 0;

// The static instance which we try to reuse for all non-simultaneous requests

static norls_allocator  theAllocator;

/*****************************************************************************/

void                nraInitTheAllocator()
{
    THE_ALLOCATOR_BASE_SIZE = norls_allocator::nraDirectAlloc() ? 
        (size_t)norls_allocator::MIN_PAGE_SIZE : (size_t)norls_allocator::DEFAULT_PAGE_SIZE;
}

void                nraTheAllocatorDone()
{   
    // We chose not to call nraTheAllocator->nraFree() and let the memory leak.
    // Below is the reason (VSW 600919).

    // The following race-condition exists during ExitProcess.
    // Thread A calls ExitProcess, which causes thread B to terminate.
    // Thread B terminated in the middle of nraToss() 
    // (through the call-chain of nraFreeTheAllocator()  ==> nraRlsm() ==> nraToss())
    // And then thread A comes along to call nraTheAllocator->nraFree() which will cause the double-free 
    // of page specified by "temp".

    // These are possible fixes:
    // 1. Thread A tries to get hold on nraTheAllocatorIsInUse lock before
    //    calling theAllocator.nraFree(). However, this could cause the deadlock because thread B
    //    has already gone and therefore it can't release nraTheAllocatorIsInUse.
    // 2. Fix the logic in nraToss() and nraFree() to update nraPageList and nraPageLast in a thread safe way.
    //    But it needs careful work to make it high performant (e.g. not holding a lock?)
    // 3. The scenario of dynamically unloading clrjit.dll cleanly is unimportant at this time.
    //    We will leak the memory associated with other instances of morls_allocator anyway.
    
    // Therefore we decided not to call the cleanup code when unloading the jit. 
    
}

/*****************************************************************************/

norls_allocator *   nraGetTheAllocator(IEEMemoryManager* pMemoryManager)
{
    if (InterlockedExchange(&nraTheAllocatorIsInUse, 1))
    {
        // Its being used by another Compiler instance
        return NULL;
    }

    if (nraTheAllocator == NULL)
    {
        // Not initialized yet

        bool res = theAllocator.nraInit(pMemoryManager, 0, 1);

        if (res)
        {
            // failed to initialize
            InterlockedExchange(&nraTheAllocatorIsInUse, 0);            
            return NULL;
        }

        nraTheAllocator = &theAllocator;
        
        assert(nraTheAllocator->nraTotalSizeAlloc() == THE_ALLOCATOR_BASE_SIZE);
        nraTheAllocator->nraMark(nraTheAllocatorMark);    
    }
    else
    {
        if (nraTheAllocator->nraGetMemoryManager() != pMemoryManager)
        {
            // already initialize with a different memory manager
            InterlockedExchange(&nraTheAllocatorIsInUse, 0);            
            return NULL;
        }
    }

    assert(nraTheAllocator->nraTotalSizeAlloc() == THE_ALLOCATOR_BASE_SIZE);
    return nraTheAllocator;
}


void                nraFreeTheAllocator()
{
    assert (nraTheAllocator != NULL);
    assert(nraTheAllocatorIsInUse == 1);

    nraTheAllocator->nraRlsm(nraTheAllocatorMark);
    assert(nraTheAllocator->nraTotalSizeAlloc() == THE_ALLOCATOR_BASE_SIZE);

    InterlockedExchange(&nraTheAllocatorIsInUse, 0);
}

/*****************************************************************************/
View Code

  开始吧,alloc.cpp是在ClrJit项目当中的,其中alloc.cpp会引用jitpch.h这个头文件。首先会定义:_MSC_VER 这个活动预处理块。如下图,关于,pragma hdrstop 的介绍可以点击链接去看看,这里就不再赘述。

 

  这里有一个空方法叫做:

void                allocatorCodeSizeBeg(){}

  我们碰到这种情况怎么去学习呢,很多人就被卡住了,其实这个问题解决方法很简单,可以利用VS的全局搜索功能。

  很遗憾,没有地方用到,这只能证明2点,第一,这个函数可能是给外部调用的,第二,这可能是内部机制的一部分,但是我并不了解,从名字上看,是分配器的相关功能,请自行补脑,我不做解释,怕误导你们。

  下面继续看代码:关于__cdecl 请看这里的介绍,如果有不懂的人,这个函数顾名思义,是debug调试停止的时候触发的函数,其中带2个参数,第一个是指针类型的char,你可以理解为string 常量(对应C#中的readonly string),第二个参数是任意类型的任意参数,即你可以传入多个参数(类似C#中的params 参数)。

void    __cdecl     debugStop(const char *why, ...)
{
    va_list     args;

    va_start(args, why);

    printf("NOTIFICATION: ");
    if  (why)
        vprintf(why, args);
    else
        printf("debugStop(0)");

    printf("\n");

    va_end(args);

    BreakIfDebuggerPresent();
}

  要研究上面的代码,你必须了解va_list这个东西,下面是va_list的定义:下面的翻译成中文就是,如果没有定义_VA_LIST_DEFINED,那么定义_VA_LIST_DEFINED,如果定义了_M_CEE_PURE 把va_list定义为ArgIterator,否则是char类型的指针变量。这里我们是第二种情况,第一种情况你就当空气,因为我也不太懂。另外关于_M_CEE_PURE的解释我在网上并没有找到,只知道在Math.h里面有过类似的定义,有知道的小伙伴可以提示下我。

#ifndef _VA_LIST_DEFINED
    #define _VA_LIST_DEFINED
    #ifdef _M_CEE_PURE
        typedef System::ArgIterator va_list;
    #else
        typedef char* va_list;
    #endif
#endif

  好吧,现在我们知道了,va_list其实就是一个char指针,类似C#当中的String,简单点理解。下面我们来分析下下面的代码。

 va_start(args, why);

  其中va_start的源码如下,它来自于stdarg.h文件,这是Visual studio的一个内置文件,:

#define va_start __crt_va_start

  我们 来看看__crt_va_start的真容:它分为__vcrt_va_start_verify_argument_type和__crt_va_start_a

#define __crt_va_start(ap, x) ((void)(__vcrt_va_start_verify_argument_type<decltype(x)>(), __crt_va_start_a(ap, x)))

  下面是上面所提到的2种类型的一些源码,可以看到,__vcrt_va_start_verify_argument_type只是做了一个类似“断言”的功能,否就抛出异常;而第二个函数__va_start里的真实代码如下图所示,有四种不同的表现形式,分布在4个.h头文件中,其中4种不同的情况下去调用。分别有_M_X64,_M_ARM64和_M_CEE_PURE || (defined _M_CEE && !defined _M_ARM && !defined _M_ARM64) 和_M_ARM,这其中牵扯到和汇编的相关知识,有兴趣的可以先学学汇编去.

 

        template <typename _Ty>
        void __vcrt_va_start_verify_argument_type() throw()
        {
            static_assert(!__vcrt_va_list_is_reference<_Ty>::__the_value, "va_start argument must not have reference type and must not be parenthesized");
        }
#define __crt_va_start_a(ap, x) ((void)(__va_start(&ap, x)))

  中间的代码我就跳过了,都是一些print语句,打印日志的,其中va_end的真实代码为,其实很好理解,就是清空,因为C++中没有垃圾回收机制。

#define va_end   __crt_va_end
#define __crt_va_end(ap)        ((void)(ap = (va_list)0))

  这个方法中的最后一个方法是:BreakIfDebuggerPresent,它的代码如下,它的意思是至少执行一次,如果有一只处于调试状态,它会进行判断,如果为0,FALSE那么继续循环,

#define BreakIfDebuggerPresent()                                            \
    do { if (IsDebuggerPresent()) DebugBreak(); }                           \
    while (0)
#endif

  其中IsDebuggerPresent和DebugBreak为windows的API函数,应该类似于WINDOWS进程挂起,其实我也没研究过。

#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
WINBASEAPI
BOOL
WINAPI
IsDebuggerPresent(
    VOID
    );

#endif

  #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)

  WINBASEAPI

  VOID
  WINAPI
  DebugBreak(
  VOID
  );

#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */

 

  下面有一个有趣的东西,作者问了个问题,这个常量是不是应该更大?欢迎各位来讨论

/* 
 * Does this constant need to be bigger?
 */
static  size_t    blockStop    = 99999999;

  其中size_t的源码如下,其实就是类似于blockStop的一个定义域吧,如果是64位windows那么就是无符号的long,否则为无符号的int类型。这个很好理解吧。

#ifdef _WIN64
    typedef unsigned __int64 size_t;
    typedef __int64          ptrdiff_t;
    typedef __int64          intptr_t;
#else
    typedef unsigned int     size_t;
    typedef int              ptrdiff_t;
    typedef int              intptr_t;
#endif

  下面初始化了变量:

size_t THE_ALLOCATOR_BASE_SIZE  = 0;

  然后是初始化nra:

bool   norls_allocator::nraInit(IEEMemoryManager* pMemoryManager, size_t pageSize, int preAlloc)
{
    bool    result = false;

    nraMemoryManager = pMemoryManager;

    nraPageList  =
    nraPageLast  = 0;

    nraFreeNext  =
    nraFreeLast  = 0;

    assert(THE_ALLOCATOR_BASE_SIZE != 0);

    nraPageSize  = pageSize ? pageSize : THE_ALLOCATOR_BASE_SIZE;

#ifdef DEBUG
    static ConfigDWORD fShouldInjectFault;
    nraShouldInjectFault = fShouldInjectFault.val(CLRConfig::INTERNAL_InjectFault) != 0;
#endif    

    if  (preAlloc)
    {
        /* Grab the initial page(s) */

        setErrorTrap(NULL, norls_allocator *, pThis, this)  // ERROR TRAP: Start normal block
        {
            pThis->nraAllocNewPage(0);
        }
        impJitErrorTrap()  // ERROR TRAP: The following block handles errors
        {
            result = true;
        }
        endErrorTrap()  // ERROR TRAP: End
    }

    return  result;
}
View Code

  我们先来看看这个方法的声明,其中后2个参数有默认值:

bool            nraInit (IEEMemoryManager* pMemoryManager, size_t pageSize = 0, int preAlloc = 0);

  我们先来看看IEEMemoryManager这个东西是何方神圣,先放整体代码,大家先不要看整体代码,怕搞不懂,我一句句来剖析:

#if defined(__cplusplus) && !defined(CINTERFACE)
    
    MIDL_INTERFACE("17713B61-B59F-4e13-BAAF-91623DC8ADC0")
    IEEMemoryManager : public IUnknown
    {
    public:
        virtual LPVOID STDMETHODCALLTYPE ClrVirtualAlloc( 
            /* [in] */ LPVOID lpAddress,
            /* [in] */ SIZE_T dwSize,
            /* [in] */ DWORD flAllocationType,
            /* [in] */ DWORD flProtect) = 0;
        
        virtual BOOL STDMETHODCALLTYPE ClrVirtualFree( 
            /* [in] */ LPVOID lpAddress,
            /* [in] */ SIZE_T dwSize,
            /* [in] */ DWORD dwFreeType) = 0;
        
        virtual SIZE_T STDMETHODCALLTYPE ClrVirtualQuery( 
            /* [in] */ const void *lpAddress,
            /* [in] */ PMEMORY_BASIC_INFORMATION lpBuffer,
            /* [in] */ SIZE_T dwLength) = 0;
        
        virtual BOOL STDMETHODCALLTYPE ClrVirtualProtect( 
            /* [in] */ LPVOID lpAddress,
            /* [in] */ SIZE_T dwSize,
            /* [in] */ DWORD flNewProtect,
            /* [in] */ DWORD *lpflOldProtect) = 0;
        
        virtual HANDLE STDMETHODCALLTYPE ClrGetProcessHeap( void) = 0;
        
        virtual HANDLE STDMETHODCALLTYPE ClrHeapCreate( 
            /* [in] */ DWORD flOptions,
            /* [in] */ SIZE_T dwInitialSize,
            /* [in] */ SIZE_T dwMaximumSize) = 0;
        
        virtual BOOL STDMETHODCALLTYPE ClrHeapDestroy( 
            /* [in] */ HANDLE hHeap) = 0;
        
        virtual LPVOID STDMETHODCALLTYPE ClrHeapAlloc( 
            /* [in] */ HANDLE hHeap,
            /* [in] */ DWORD dwFlags,
            /* [in] */ SIZE_T dwBytes) = 0;
        
        virtual BOOL STDMETHODCALLTYPE ClrHeapFree( 
            /* [in] */ HANDLE hHeap,
            /* [in] */ DWORD dwFlags,
            /* [in] */ LPVOID lpMem) = 0;
        
        virtual BOOL STDMETHODCALLTYPE ClrHeapValidate( 
            /* [in] */ HANDLE hHeap,
            /* [in] */ DWORD dwFlags,
            /* [in] */ const void *lpMem) = 0;
        
        virtual HANDLE STDMETHODCALLTYPE ClrGetProcessExecutableHeap( void) = 0;
        
    };
    
    
View Code

  首先先决条件是,定义了C++而且没有定义CINTERFACE,CINTERFACE是什么我暂时还不太懂,不过没关系,这并不妨碍我学习源码。

#if defined(__cplusplus) && !defined(CINTERFACE)

  从下面的我们 可以得到一个结论,它是一个类,并且它被IUnknown所继承。

IEEMemoryManager : public IUnknown

  它有几个方法分别为:

  • ClrVirtualAlloc
  • ClrVirtualFree
  • ClrVirtualQuery
  • ClrVirtualProtect
  • ClrGetProcessHeap
  • ClrHeapCreate
  • ClrHeapDestroy
  • ClrHeapAlloc
  • ClrHeapFree
  • ClrHeapValidate
  • ClrGetProcessExecutableHeap

 

    英语好的同学,理解这些东西应该不是很难,其实你也没必要深入研究,反正认为:这货就是在CLR生命周期中一些必须做的“事情”而已。下面我们再回到nraInit方法里,下面的应该不用我多说了,初始化而已。

 nraMemoryManager = pMemoryManager;

    nraPageList  =
    nraPageLast  = 0;

    nraFreeNext  =
    nraFreeLast  = 0;

    assert(THE_ALLOCATOR_BASE_SIZE != 0);

    nraPageSize  = pageSize ? pageSize : THE_ALLOCATOR_BASE_SIZE;

  如果处于调试模式,还会走如下代码,会从CLR的配置文件中(我也不知道在哪),寻找配置。

#ifdef DEBUG
    static ConfigDWORD fShouldInjectFault;
    nraShouldInjectFault = fShouldInjectFault.val(CLRConfig::INTERNAL_InjectFault) != 0;
#endif 

  下面是INTERNAL_InjectFault的源码。

CONFIG_DWORD_INFO_EX(INTERNAL_InjectFault, W("InjectFault"), 0, "", CLRConfig::REGUTIL_default)

  其中不光是我困惑,看来大大们也困惑,大大还在犹豫是否要“注入”失败”的标志位,具体原因当然我也不知道。

#ifdef DEBUG
    bool            nraShouldInjectFault; // Should we inject fault?
#endif

  如果参数里面preAlloc为true.

    if  (preAlloc)
    {
        /* Grab the initial page(s) */

        setErrorTrap(NULL, norls_allocator *, pThis, this)  // ERROR TRAP: Start normal block
        {
            pThis->nraAllocNewPage(0);
        }
        impJitErrorTrap()  // ERROR TRAP: The following block handles errors
        {
            result = true;
        }
        endErrorTrap()  // ERROR TRAP: End
    }

 下面我来分析下第一个函数setErrorTrap,其实这只是初始化JIT的一个异常处理机制的部分,从下面的代码中可以窥探下JIT的一些东西。代码没有截全。有兴趣的可以搜索下error.h文件,本文不再分析。

#define                 setErrorTrap(compHnd, ParamType, paramDef, paramRef) \
    struct __JITParam : ErrorTrapParam                                      \
    {                                                                       \
        ParamType param;                                                    \
    } __JITparam;                                                           \
    __JITparam.errc = CORJIT_INTERNALERROR;                                 \
    __JITparam.jitInfo = compHnd;                                           \
    __JITparam.param = paramRef;                                            \
    PAL_TRY(__JITParam *, __JITpParam, &__JITparam)                         \
    {                                                                       \
        ParamType paramDef = __JITpParam->param;

  下面让我们看下nraAllocNewPage这个方法:

void    *   norls_allocator::nraAllocNewPage(size_t sz)
{
    norls_pagdesc * newPage;
    size_t          sizPage;

    size_t          realSize = sz + sizeof(norls_pagdesc);
    if (realSize < sz) 
        NOMEM();   // Integer overflow

    /* Do we have a page that's now full? */

    if  (nraPageLast)
    {
        /* Undo the "+=" done in nraAlloc() */

        nraFreeNext -= sz;

        /* Save the actual used size of the page */

        nraPageLast->nrpUsedSize = nraFreeNext - nraPageLast->nrpContents;
    }

    /* Make sure we grab enough to satisfy the allocation request */

    sizPage = nraPageSize;

    if  (sizPage < realSize)
    {
        /* The allocation doesn't fit in a default-sized page */

#ifdef  DEBUG
//      if  (nraPageLast) printf("NOTE: wasted %u bytes in last page\n", nraPageLast->nrpPageSize - nraPageLast->nrpUsedSize);
#endif

        sizPage = realSize;
    }

    /* Round to the nearest multiple of OS page size */

    if (!nraDirectAlloc())
    {
        sizPage +=  (DEFAULT_PAGE_SIZE - 1);
        sizPage &= ~(DEFAULT_PAGE_SIZE - 1);
    }

    /* Allocate the new page */

    newPage = (norls_pagdesc *)nraVirtualAlloc(0, sizPage, MEM_COMMIT, PAGE_READWRITE);
    if  (!newPage)
        NOMEM();

#ifdef DEBUG
    newPage->nrpSelfPtr = newPage;
#endif

    /* Append the new page to the end of the list */

    newPage->nrpNextPage = 0;
    newPage->nrpPageSize = sizPage;
    newPage->nrpPrevPage = nraPageLast;
    newPage->nrpUsedSize = 0;  // nrpUsedSize is meaningless until a new page is allocated.
                               // Instead of letting it contain garbage (so to confuse us),
                               // set it to zero.

    if  (nraPageLast)
        nraPageLast->nrpNextPage = newPage;
    else
        nraPageList              = newPage;
    nraPageLast = newPage;

    /* Set up the 'next' and 'last' pointers */

    nraFreeNext = newPage->nrpContents + sz;
    nraFreeLast = newPage->nrpPageSize + (BYTE *)newPage;

    assert(nraFreeNext <= nraFreeLast);

    return  newPage->nrpContents;
}
View Code

  我只介绍几个重要的地方,第一个为norls_pagdesc这个结构体,它的访问权限是private。个人感觉这个东西是做链表功能用的,具体作用不详。

    struct norls_pagdesc
    {
        norls_pagdesc * nrpNextPage;
        norls_pagdesc * nrpPrevPage;
#ifdef DEBUG
        void    *       nrpSelfPtr;
#endif
        size_t          nrpPageSize;    // # of bytes allocated
        size_t          nrpUsedSize;    // # of bytes actually used. (This is only valid when we've allocated a new page.)
                                        // See norls_allocator::nraAllocNewPage.
        BYTE            nrpContents[];
    };

  其中因为传入参数为0,所以真实大小为sizeof(norls_pagdesc)即上面结构体的成员变量的大小之和。

 size_t          realSize = sz + sizeof(norls_pagdesc);

  如果出现参数异常,那么执行NOMEM,通知异常发生。

if (realSize < sz) 
        NOMEM();   // Integer overflow
void DECLSPEC_NORETURN NOMEM()
{
#if MEASURE_FATAL
    fatal_NOMEM += 1;
#endif // MEASURE_FATAL

    fatal(CORJIT_OUTOFMEM);
}

  如果是到了链表表尾,其实作者很怀疑是否真正是这样的?

  if  (nraPageLast) /* Do we have a page that's now full? */
    {
        /* Undo the "+=" done in nraAlloc() */

        nraFreeNext -= sz;

        /* Save the actual used size of the page */

        nraPageLast->nrpUsedSize = nraFreeNext - nraPageLast->nrpContents;
    }

  我们先来看看nraFreeNext 的介绍,作者说到:如果不为0,那么永远指向LAST。

    BYTE    *       nraFreeNext;        // these two (when non-zero) will
    BYTE    *       nraFreeLast;        // always point into 'nraPageLast'

  好,现在回到alloc.cpp文件中,它的主要作用是为了更新新的使用过的空间大小。

        nraPageLast->nrpUsedSize = nraFreeNext - nraPageLast->nrpContents;

  下面的代码是把norls_allocator里的nraPageSize赋值给sizePage以保障空间足够。

    /* Make sure we grab enough to satisfy the allocation request */

    sizPage = nraPageSize;

  如果sizPage小于真实大小,那么把真实大小赋值给sizPage,这是什么原因造成的,其实我觉得这么写不太合理,作者也许做了一个硬编码吧。

    if  (sizPage < realSize)
    {
        /* The allocation doesn't fit in a default-sized page */

#ifdef  DEBUG
//      if  (nraPageLast) printf("NOTE: wasted %u bytes in last page\n", nraPageLast->nrpPageSize - nraPageLast->nrpUsedSize);
#endif

        sizPage = realSize;
    }

  下面我们来看看,如果nraDirectAlloc是FALSE,那么执行如下代码,我们先来看看,sizePage是先+了DEFAULT_PAGE_SIZE 个单位,然后按位与了DEFAULT_PAGE_SIZE-1的反码个单位。如果你还不清楚什么是按位计算,那么请你复习下大学基础知识,本文不再做深入讨论。

    if (!nraDirectAlloc())
    {
        sizPage +=  (DEFAULT_PAGE_SIZE - 1);
        sizPage &= ~(DEFAULT_PAGE_SIZE - 1);
    }

  下面我们再来看看nraDirectAlloc这个方法吧。其实就是返回一个BOOL类型的东西,至于这个方法的整体作用,可以看下面的英文注释。和上面的Config一样,都是做了配置。

inline bool norls_allocator::nraDirectAlloc()
{
    // When JitDirectAlloc is set, all JIT allocations requests are forwarded
    // directly to the OS. This allows taking advantage of pageheap and other gflag
    // knobs for ensuring that we do not have buffer overruns in the JIT.

    static ConfigDWORD fJitDirectAlloc;
    return (fJitDirectAlloc.val(CLRConfig::INTERNAL_JitDirectAlloc) != 0);
}

   下面是分配新的page.

    newPage = (norls_pagdesc *)nraVirtualAlloc(0, sizPage, MEM_COMMIT, PAGE_READWRITE);

  方法如下:如果满足nraDirectAlloc==true,那么执行HeapAlloc方法,其中参数GetProcessHeap和HeapAlloc(堆分配)为调用WINDOW API。有兴趣的可以自己看看源码。

HeapAlloc(
    _In_ HANDLE hHeap,
    _In_ DWORD dwFlags,
    _In_ SIZE_T dwBytes
    );

 如果不满足nraDirectAlloc,那庅调用DbgNew方法,源码为:

 

inline void * DbgNew(size_t size)
{
    return ClrAllocInProcessHeap(0, S_SIZE_T(size));
}

我把ClrAllocInProcessHeap的代码贴一下,有兴趣的可以自己去看看。我就不多做解释了。

inline LPVOID ClrAllocInProcessHeap(DWORD dwFlags, S_SIZE_T dwBytes)
{
    STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
    if (dwBytes.IsOverflow())
    {
        return NULL;
    }

#ifndef SELF_NO_HOST
    return __ClrAllocInProcessHeap(dwFlags, dwBytes.Value());
#else
#undef HeapAlloc
#undef GetProcessHeap
    static HANDLE ProcessHeap = NULL;
    if (ProcessHeap == NULL)
        ProcessHeap = GetProcessHeap();
    return ::HeapAlloc(ProcessHeap,dwFlags,dwBytes.Value());
#define HeapAlloc(hHeap, dwFlags, dwBytes) Dont_Use_HeapAlloc(hHeap, dwFlags, dwBytes)
#define GetProcessHeap() Dont_Use_GetProcessHeap()
#endif
}
View Code

 

    LPVOID          nraVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
    {
#if defined(DEBUG)
        assert(lpAddress == 0 && flAllocationType == MEM_COMMIT && flProtect == PAGE_READWRITE);
        if (nraDirectAlloc())
        {
#undef GetProcessHeap
#undef HeapAlloc
            return ::HeapAlloc(GetProcessHeap(), 0, dwSize);
        }
        else
            return DbgNew(dwSize);
#else
        return nraMemoryManager->ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
#endif
    }

  下面的代码就是做一些基本的赋值和检查。

    if  (!newPage)
        NOMEM();

#ifdef DEBUG
    newPage->nrpSelfPtr = newPage;
#endif

  下面就是把新的page追加到list的后端。

    /* Append the new page to the end of the list */

    newPage->nrpNextPage = 0;
    newPage->nrpPageSize = sizPage;
    newPage->nrpPrevPage = nraPageLast;
    newPage->nrpUsedSize = 0;  // nrpUsedSize is meaningless until a new page is allocated.
                               // Instead of letting it contain garbage (so to confuse us),
                               // set it to zero.

    if  (nraPageLast)
        nraPageLast->nrpNextPage = newPage;
    else
        nraPageList              = newPage;
    nraPageLast = newPage;

  最后重新设置一下next和last指针,总之这个是个公共方法,只是nraInit里面只用到了为0的情况: pThis->nraAllocNewPage(0);

 

    /* Set up the 'next' and 'last' pointers */

    nraFreeNext = newPage->nrpContents + sz;
    nraFreeLast = newPage->nrpPageSize + (BYTE *)newPage;

    assert(nraFreeNext <= nraFreeLast);

    return  newPage->nrpContents;

  回到init函数然后执行如下,结束nraInit方法

        impJitErrorTrap()  // ERROR TRAP: The following block handles errors
        {
            result = true;
        }
        endErrorTrap()  // ERROR TRAP: End

  写完了,知道写得不太好,请指出错误,轻喷。晚安。

posted @ 2017-06-03 22:13  KMSFan  阅读(1180)  评论(1编辑  收藏  举报
document.getElementById("homeTopTitle").innerText="ღKawaii";