• 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("
    ");
    
        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
    ", 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("
    ");
    
        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
    ", 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
    ", 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

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

  • 相关阅读:
    Python Scrapy爬虫(下)
    Hadoop HDFS
    Spark核心 RDD(上)
    自定义日期格式------SimpleDateFormat
    常用类-- 使用comparator实现定制排序
    自定义日期格式------DateTimeFormatter
    多线程-方式四使用线程池
    多线程-方式三实现Callable接口方式 JDK5.0新增
    解决线程安全------lock锁
    死锁问题
  • 原文地址:https://www.cnblogs.com/kmsfan/p/6938692.html
Copyright © 2020-2023  润新知