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

时间:2021-06-27 10:51:49

  首先声明,自己对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 = ; /*****************************************************************************/
#endif // DEBUG
/*****************************************************************************/ size_t THE_ALLOCATOR_BASE_SIZE = ; bool norls_allocator::nraInit(IEEMemoryManager* pMemoryManager, size_t pageSize, int preAlloc)
{
bool result = false; nraMemoryManager = pMemoryManager; nraPageList =
nraPageLast = ; nraFreeNext =
nraFreeLast = ; assert(THE_ALLOCATOR_BASE_SIZE != ); nraPageSize = pageSize ? pageSize : THE_ALLOCATOR_BASE_SIZE; #ifdef DEBUG
static ConfigDWORD fShouldInjectFault;
nraShouldInjectFault = fShouldInjectFault.val(CLRConfig::INTERNAL_InjectFault) != ;
#endif if (preAlloc)
{
/* Grab the initial page(s) */ setErrorTrap(NULL, norls_allocator *, pThis, this) // ERROR TRAP: Start normal block
{
pThis->nraAllocNewPage();
}
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 - );
sizPage &= ~(DEFAULT_PAGE_SIZE - );
} /* Allocate the new page */ newPage = (norls_pagdesc *)nraVirtualAlloc(, 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 = ;
newPage->nrpPageSize = sizPage;
newPage->nrpPrevPage = nraPageLast;
newPage->nrpUsedSize = ; // 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, , 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 = ; nraVirtualFree(temp, , MEM_RELEASE);
} nraFreeNext = mark.nmNext;
nraFreeLast = mark.nmLast;
} /*****************************************************************************/
#ifdef DEBUG
/*****************************************************************************/
void * norls_allocator::nraAlloc(size_t sz)
{
void * block; assert(sz != && (sz & (sizeof(int) - )) == );
#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();
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 = ; for (page = nraPageList; page; page = page->nrpNextPage)
size += page->nrpPageSize; return size;
} size_t norls_allocator::nraTotalSizeUsed()
{
norls_pagdesc * page;
size_t size = ; 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 = ; // 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, ))
{
// Its being used by another Compiler instance
return NULL;
} if (nraTheAllocator == NULL)
{
// Not initialized yet bool res = theAllocator.nraInit(pMemoryManager, , ); if (res)
{
// failed to initialize
InterlockedExchange(&nraTheAllocatorIsInUse, );
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, );
return NULL;
}
} assert(nraTheAllocator->nraTotalSizeAlloc() == THE_ALLOCATOR_BASE_SIZE);
return nraTheAllocator;
} void nraFreeTheAllocator()
{
assert (nraTheAllocator != NULL);
assert(nraTheAllocatorIsInUse == ); nraTheAllocator->nraRlsm(nraTheAllocatorMark);
assert(nraTheAllocator->nraTotalSizeAlloc() == THE_ALLOCATOR_BASE_SIZE); InterlockedExchange(&nraTheAllocatorIsInUse, );
} /*****************************************************************************/

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

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

  这里有一个空方法叫做:

void                allocatorCodeSizeBeg(){}

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

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

  很遗憾,没有地方用到,这只能证明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,这其中牵扯到和汇编的相关知识,有兴趣的可以先学学汇编去.

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

        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 ()
#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 = ;

  其中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  = ;

  然后是初始化nra:

bool   norls_allocator::nraInit(IEEMemoryManager* pMemoryManager, size_t pageSize, int preAlloc)
{
bool result = false; nraMemoryManager = pMemoryManager; nraPageList =
nraPageLast = ; nraFreeNext =
nraFreeLast = ; assert(THE_ALLOCATOR_BASE_SIZE != ); nraPageSize = pageSize ? pageSize : THE_ALLOCATOR_BASE_SIZE; #ifdef DEBUG
static ConfigDWORD fShouldInjectFault;
nraShouldInjectFault = fShouldInjectFault.val(CLRConfig::INTERNAL_InjectFault) != ;
#endif if (preAlloc)
{
/* Grab the initial page(s) */ setErrorTrap(NULL, norls_allocator *, pThis, this) // ERROR TRAP: Start normal block
{
pThis->nraAllocNewPage();
}
impJitErrorTrap() // ERROR TRAP: The following block handles errors
{
result = true;
}
endErrorTrap() // ERROR TRAP: End
} return result;
}

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

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

  我们先来看看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) = ; virtual BOOL STDMETHODCALLTYPE ClrVirtualFree(
/* [in] */ LPVOID lpAddress,
/* [in] */ SIZE_T dwSize,
/* [in] */ DWORD dwFreeType) = ; virtual SIZE_T STDMETHODCALLTYPE ClrVirtualQuery(
/* [in] */ const void *lpAddress,
/* [in] */ PMEMORY_BASIC_INFORMATION lpBuffer,
/* [in] */ SIZE_T dwLength) = ; virtual BOOL STDMETHODCALLTYPE ClrVirtualProtect(
/* [in] */ LPVOID lpAddress,
/* [in] */ SIZE_T dwSize,
/* [in] */ DWORD flNewProtect,
/* [in] */ DWORD *lpflOldProtect) = ; virtual HANDLE STDMETHODCALLTYPE ClrGetProcessHeap( void) = ; virtual HANDLE STDMETHODCALLTYPE ClrHeapCreate(
/* [in] */ DWORD flOptions,
/* [in] */ SIZE_T dwInitialSize,
/* [in] */ SIZE_T dwMaximumSize) = ; virtual BOOL STDMETHODCALLTYPE ClrHeapDestroy(
/* [in] */ HANDLE hHeap) = ; virtual LPVOID STDMETHODCALLTYPE ClrHeapAlloc(
/* [in] */ HANDLE hHeap,
/* [in] */ DWORD dwFlags,
/* [in] */ SIZE_T dwBytes) = ; virtual BOOL STDMETHODCALLTYPE ClrHeapFree(
/* [in] */ HANDLE hHeap,
/* [in] */ DWORD dwFlags,
/* [in] */ LPVOID lpMem) = ; virtual BOOL STDMETHODCALLTYPE ClrHeapValidate(
/* [in] */ HANDLE hHeap,
/* [in] */ DWORD dwFlags,
/* [in] */ const void *lpMem) = ; virtual HANDLE STDMETHODCALLTYPE ClrGetProcessExecutableHeap( void) = ; };

  首先先决条件是,定义了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 = ; nraFreeNext =
nraFreeLast = ; assert(THE_ALLOCATOR_BASE_SIZE != ); nraPageSize = pageSize ? pageSize : THE_ALLOCATOR_BASE_SIZE;

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

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

  下面是INTERNAL_InjectFault的源码。

CONFIG_DWORD_INFO_EX(INTERNAL_InjectFault, W("InjectFault"), , "", 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();
}
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 - );
sizPage &= ~(DEFAULT_PAGE_SIZE - );
} /* Allocate the new page */ newPage = (norls_pagdesc *)nraVirtualAlloc(, 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 = ;
newPage->nrpPageSize = sizPage;
newPage->nrpPrevPage = nraPageLast;
newPage->nrpUsedSize = ; // 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 += ;
#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 - );
sizPage &= ~(DEFAULT_PAGE_SIZE - );
}

  下面我们再来看看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) != );
}

  下面是分配新的page.

    newPage = (norls_pagdesc *)nraVirtualAlloc(, 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(, 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 == && flAllocationType == MEM_COMMIT && flProtect == PAGE_READWRITE);
if (nraDirectAlloc())
{
#undef GetProcessHeap
#undef HeapAlloc
return ::HeapAlloc(GetProcessHeap(), , 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 = ;
newPage->nrpPageSize = sizPage;
newPage->nrpPrevPage = nraPageLast;
newPage->nrpUsedSize = ; // 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

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