首先声明,自己对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 的介绍可以点击链接去看看,这里就不再赘述。

  这里有一个空方法叫做:

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

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

coreCLR系列随笔 之ClrJit项目之alloc.cpp文件分析(1)的更多相关文章

  1. 【置顶】CoreCLR系列随笔

    CoreCLR配置系列 在Windows上编译和调试CoreCLR GC探索系列 C++随笔:.NET CoreCLR之GC探索(1) C++随笔:.NET CoreCLR之GC探索(2) C++随笔 ...

  2. CoreCLR中超过3万行代码的gc.cpp文件的来源

    在CoreCLR的开源代码中,GC的主要实现代码gc.cpp文件大小竟然有1.17MB,打开文件一看,竟然有35490行!第一次见到如此多行的单个代码文件. github都不让直接查看:https:/ ...

  3. ASP.NET Core3.1使用IdentityServer4中间件系列随笔(二):创建API项目,配置IdentityServer保护API资源

    配套源码:https://gitee.com/jardeng/IdentitySolution 接上一篇<ASP.NET Core3.1使用IdentityServer4中间件系列随笔(一):搭 ...

  4. 使用Beautiful Soup编写一个爬虫 系列随笔汇总

    这几篇博文只是为了记录学习Beautiful Soup的过程,不仅方便自己以后查看,也许能帮到同样在学习这个技术的朋友.通过学习Beautiful Soup基础知识 完成了一个简单的爬虫服务:从all ...

  5. ASP.NET Core3.1使用IdentityServer4中间件系列随笔(三):创建使用[ClientCredentials客户端凭证]授权模式的客户端

    配套源码:https://gitee.com/jardeng/IdentitySolution 上一篇<ASP.NET Core3.1使用IdentityServer4中间件系列随笔(二):创建 ...

  6. AI人工智能系列随笔

    初探 AI人工智能系列随笔:syntaxnet 初探(1)

  7. ASP.NET MVC 系列随笔汇总[未完待续……]

    ASP.NET MVC 系列随笔汇总[未完待续……] 为了方便大家浏览所以整理一下,有的系列篇幅中不是很全面以后会慢慢的补全的. 学前篇之: ASP.NET MVC学前篇之扩展方法.链式编程 ASP. ...

  8. WPF Step By Step 系列-Prism框架在项目中使用

    WPF Step By Step 系列-Prism框架在项目中使用 回顾 上一篇,我们介绍了关于控件模板的用法,本节我们将继续说明WPF更加实用的内容,在大型的项目中如何使用Prism框架,并给予Pr ...

  9. MyBitis(iBitis)系列随笔之五:多表(一对多关联查询)

    MyBitis(iBitis)系列随笔之一:MyBitis入门实例 MyBitis(iBitis)系列随笔之二:类型别名(typeAliases)与表-对象映射(ORM) MyBitis(iBitis ...

随机推荐

  1. MapReduce过程(包括Shuffle)详解

    首先,map的输入数据默认一个一个的键值对,键就是每一行首字母的偏移量,值就是每一行的值了. 然后每一个输入的键值对都会用我们定义的map函数去处理,这里用wordcount来举例的话就是,每一个键值 ...

  2. PHP运算符与表达式

    一.概述: 在我们平时的开发中,最离不开的就是运算,在编写比较复杂的后台程序的时候,算法更是必不可少的.涉及到运算就应该了解PHP的运算符,下面我们来一起看一下PHP中常见的运算符,以及和其他语言的区 ...

  3. HTTP笔记

    "你知道当我们在网页浏览器(Web browser)的地址栏中输入 URL 时,Web 页面是如何呈现的吗?" HTTP协议 HTTP协议(HyperText Transfer P ...

  4. CSS表单属性

    一般来说,表单在一个页面中是必不可少的,下面是我对表单的知识总结: 依次要说的是表单元素.表单属性.以及表单提交(js知识) 1,表单元素: <form action="提交的位置 / ...

  5. 简单的jquery左侧导航栏和页面选中效果

    这里是要实现导航的左侧并选中的,此功能需引用jquery 效果: 左侧导航 <div class="box"> <ul class="menu" ...

  6. 蓝桥杯-无穷分数-java

    /* (程序头部注释开始) * 程序的版权和版本声明部分 * Copyright (c) 2016, 广州科技贸易职业学院信息工程系学生 * All rights reserved. * 文件名称: ...

  7. ESXi5.0误删除虚拟机还有办法恢复吗?答案是可以!

    [数据恢复故障描述]故障的虚拟化系统是 ESXi5.0,连接了多个LUN,其中一个1T的LUN上跑有7 台虚拟机,均为Windows Server 2003,管理员因为其它原因误删除了一台虚拟机,此台 ...

  8. Spring Boot 整合 MyBatis

    前言 现在业界比较流行的数据操作层框架 MyBatis,下面就讲解下 Springboot 如何整合 MyBatis,这里使用的是xml配置SQL而不是用注解.主要是 SQL 和业务代码应该隔离,方便 ...

  9. dp

    1. 将原问题分解为子问题 2. 确定状态 3. 确定一些初始状态(边界状态)的值 4. 确定状态转移方程 1) 问题具有最优子结构性质.如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具 ...

  10. 简单的3D图片轮播dome

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...