首先声明,自己对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); } /*****************************************************************************/
开始吧,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; }
我们先来看看这个方法的声明,其中后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; };
首先先决条件是,定义了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; }
我只介绍几个重要的地方,第一个为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 }
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
写完了,知道写得不太好,请指出错误,轻喷。晚安。