自从可以动态调试SSCLI之后,发现这个玩意还真是个宝山,越玩越有意思,就像捅开一扇门,发现门后面还有一座宝山……不光CLR的内部实现细节,可以象是放电影一样呈现在眼前,Visual Studio里面的一些底层的技术,还有OS的底层技术,查看起来那是相当的便捷。
这里就说说malloc函数是具体如何实现的,能够F10,F11动态的在VS里面跟踪其一步一步的实现的源码,真是一件痛快的事情。
为嘛要说malloc的实现呢?因为在main开始以后,要为传递进来的参数分配内存地址。而这个时候我不小心按了F11,不小心看到了malloc是具体如何运作的:
Clix.cpp的int __cdecl main(int argc, char **argv)之后,
首先得到参数行,然后分配一个空间把这个参数行保存起来:
pwzCmdLine = ::GetCommandLineW();
// Allocate for the worst case storage requirement.
WCHAR *pAlloc = (WCHAR*)malloc((wcslen(pwzCmdLine) + 1) * sizeof(WCHAR));
首先获得到命令行,然后使用malloc分配ygie空间给存起来。sizeof(WCHAR)的含义ms是在后面加上一个终止的标记。
Malloc函数,首先跳转到了d:/Rotor/sscli20/pal/win32/win32pal.c下:
One:
PALIMPORT
void *
__cdecl
PAL_malloc(size_t bytes)
{
//mark how much bytes had successful allocated.
void *Ret;
LOGAPI("malloc(bytes=%p)/n", bytes);
Ret = malloc(bytes);
LOGAPI("malloc returns void* %p/n", Ret);
return Ret;
}
这是一个PAL_malloc,是sscli里面针对特定的操作系统的PAL层的实现,因为我用的是操作系统是Windows XP En,这里就开始调用操作系统里面的malloc的实现了,在看malloc之前,先看看LOGAPI是干嘛的:
Two:
void
__cdecl
PalLogApi(const char *fmt,...)
{
va_list list;
// Assert that the PAL APIs are only being called when the PAL
// is properly initialized.
// This assert is disabled because of gcc startup code calls Win32 functions before calling PAL initialize.
// PALASSERT(PalReferenceCount != 0);
if (LogFileHandle == INVALID_HANDLE_VALUE) {
// Logging isn't enabled
return;
}
va_start(list, fmt);
PalLogApiCore(fmt, list);
va_end(list);
}
首先,我很奇怪C++里面的…是个怎么样子的参数传递方法….
打开一看,咱就知道了,这个是和SSCLI的调试功能紧密结合在一起的。下面截图了个:
这下看到了,LogFileHandle表示如果开启了SSCLI的log功能,就把这次malloc的情况给记录下来。
然后这里没有开启LOG功能,这个变量就是0xfffffff,然后就直接return了。继续回到One里面的Ret = malloc(bytes);这个地方。这个方法的目的,是Allocate of block of memory of at least size bytes from the heap and return a pointer to it.下跳转到了Malloc的本地实现了,到了文件:
Three:
C:/Program Files/Microsoft Visual Studio 9.0/VC/crt/src/dbgmalloc.c
extern "C" _CRTIMP void * __cdecl malloc (size_t nSize)
{
void *res = _nh_malloc_dbg(nSize, _newmode, _NORMAL_BLOCK, NULL, 0);
RTCCALLBACK(_RTC_Allocate_hook, (res, nSize, 0));
return res;
}
然后继续看调用的_nh_malloc_dbg来如何实现内存的分配,这下来到了
C:/Program Files/Microsoft Visual Studio 9.0/VC/crt/src/dbgheap.c,这个文件中:
Four:
extern "C" void * __cdecl _nh_malloc_dbg (
size_t nSize,
int nhFlag,
int nBlockUse,
const char * szFileName,
int nLine
)
{
int errno_tmp = 0;
void * pvBlk = _nh_malloc_dbg_impl(nSize, nhFlag, nBlockUse, szFileName, nLine, &errno_tmp);
if ( pvBlk == NULL && errno_tmp != 0 && _errno())
{
errno = errno_tmp; // recall, #define errno *_errno()
}
return pvBlk;
}
从名字上来看,是在Debug heap上面给分配的空间。这个方法的purpose,is Allocate of block of memory of at least size bytes from the debug heap and return a pointer to it. Assumes heap already locked.If no blocks available, call new handler.Allocates any type of supported memory block.
Five:
extern "C" static void * __cdecl _nh_malloc_dbg_impl (
size_t nSize,
int nhFlag,
int nBlockUse,
const char * szFileName,
int nLine,
int * errno_tmp
)
{
void * pvBlk;
for (;;)
{
/* do the allocation*/
pvBlk = _heap_alloc_dbg_impl(nSize, nBlockUse, szFileName, nLine, errno_tmp);
if (pvBlk)
{
return pvBlk;
}
if (nhFlag == 0)
{
*errno_tmp = ENOMEM;
return pvBlk;
}
/* call installed new handler */
if (!_callnewh(nSize))
{
*errno_tmp = ENOMEM;
return NULL;
}
/* new handler was successful -- try to allocate again */
}
}
_nh_malloc_dbg_impl()这个方法,主要是在Debug heap上面分配一段的memory,同时返回一个指向它的指针,同时,假设这个heap已经被lock了。这里,又跳转到了_heap_alloc_dbg_impl,唉,我等的花都谢了,还是这个文件里面,终于,在这里看到了实际的内存分配的过程:…
Six:
extern "C" static void * __cdecl _heap_alloc_dbg_impl(
size_t nSize,
int nBlockUse,
const char * szFileName,
int nLine,
int * errno_tmp
)
{
long lRequest;
size_t blockSize;
int fIgnore = FALSE;
_CrtMemBlockHeader * pHead;
void *retval=NULL;
/* lock the heap*/
_mlock(_HEAP_LOCK);
__try {
/* verify heap before allocation */
if (check_frequency > 0)//false
if (check_counter == (check_frequency - 1))
{
_ASSERTE(_CrtCheckMemory());
check_counter = 0;
}
else
check_counter++;
lRequest = _lRequestCurr;
/* break into debugger at specific memory allocation */
if (_crtBreakAlloc != -1L && lRequest == _crtBreakAlloc)//false
_CrtDbgBreak();
/* forced failure */
if ((_pfnAllocHook) && !(*_pfnAllocHook)(_HOOK_ALLOC, NULL, nSize,
nBlockUse, lRequest, (const unsigned char *)szFileName, nLine)) //false
{
if (szFileName)
_RPT2(_CRT_WARN, "Client hook allocation failure at file %hs line %d./n",
szFileName, nLine);
else
_RPT0(_CRT_WARN, "Client hook allocation failure./n");
}
else
{
/* cannot ignore CRT allocations */
if (_BLOCK_TYPE(nBlockUse) != _CRT_BLOCK &&
!(_crtDbgFlag & _CRTDBG_ALLOC_MEM_DF)) //false
fIgnore = TRUE;
/* Diagnostic memory allocation from this point on */
if (nSize > (size_t)(_HEAP_MAXREQ - nNoMansLandSize - sizeof(_CrtMemBlockHeader))) //false
{
_RPT1(_CRT_ERROR, "Invalid allocation size: %Iu bytes./n", nSize);
*errno_tmp = ENOMEM;
}
else
{
if (!_BLOCK_TYPE_IS_VALID(nBlockUse)) //false
{
_RPT0(_CRT_ERROR, "Error: memory allocation: bad memory block type./n");
}
blockSize = sizeof(_CrtMemBlockHeader) + nSize + nNoMansLandSize;
RTCCALLBACK(_RTC_FuncCheckSet_hook,(0));
//acture called:
//return HeapAlloc(_crtheap, 0, size ? size : 1);
pHead = (_CrtMemBlockHeader *)_heap_alloc_base(blockSize);
if (pHead == NULL) //false
{
*errno_tmp = ENOMEM;
RTCCALLBACK(_RTC_FuncCheckSet_hook,(1));
}
else
{
/* commit allocation */
++_lRequestCurr;
if (fIgnore) //false
{
pHead->pBlockHeaderNext = NULL;
pHead->pBlockHeaderPrev = NULL;
pHead->szFileName = NULL;
pHead->nLine = IGNORE_LINE;
pHead->nDataSize = nSize;
pHead->nBlockUse = _IGNORE_BLOCK;
pHead->lRequest = IGNORE_REQ;
}
else {
/* keep track of total amount of memory allocated */
if (SIZE_MAX - _lTotalAlloc > nSize)
{
_lTotalAlloc += nSize;
}
else
{
_lTotalAlloc = SIZE_MAX;
}
_lCurAlloc += nSize;
if (_lCurAlloc > _lMaxAlloc)
_lMaxAlloc = _lCurAlloc;
if (_pFirstBlock) //false
_pFirstBlock->pBlockHeaderPrev = pHead;
else
_pLastBlock = pHead;
pHead->pBlockHeaderNext = _pFirstBlock;
pHead->pBlockHeaderPrev = NULL;
pHead->szFileName = (char *)szFileName;
pHead->nLine = nLine;
pHead->nDataSize = nSize;
pHead->nBlockUse = nBlockUse;
pHead->lRequest = lRequest;
//the type of pHead is _CrtMemBlockHeader *
/* link blocks together */
_pFirstBlock = pHead;
}
/* fill in gap before and after real block */
//memset founction is usually used to set a gap of mem to a certain char, usually used for initial memory.
memset((void *)pHead->gap, _bNoMansLandFill, nNoMansLandSize);
memset((void *)(pbData(pHead) + nSize), _bNoMansLandFill, nNoMansLandSize);
/* fill data with silly value (but non-zero) */
memset((void *)pbData(pHead), _bCleanLandFill, nSize);
retval=(void *)pbData(pHead);
}
}
}
}
__finally {
/* unlock the heap */
_munlock(_HEAP_LOCK);
}
return retval;
}
先看看_mlock(_HEAP_LOCK);是如何实现的:
void __cdecl _lock (
int locknum
)
{
/*
* Create/open the lock, if necessary
*/
if ( _locktable[locknum].lock == NULL ) {
if ( !_mtinitlocknum(locknum) )
_amsg_exit( _RT_LOCK );
}
/*
* Enter the critical section.
*/
EnterCriticalSection( _locktable[locknum].lock );
}
看到了传说中的locktable,截图留念下:
也得到了Locktable是如何操作的方法,如上面所示…
在Six:中,我标出了每个if判断的时候所走的分支。做好了这一切之后,分配好了空间就一步一步返回了。最后提下memset方法,链接到了C:/Program Files/Microsoft Visual Studio 9.0/VC/crt/src/INTEL/memset.asm中,一大段汇编代码来移动内存。主要用来做内存的初始化的时候用。
Lbq1221119@cnblogs first post at http://sscli.cnblogs.com
2008-11-12 10:35:22 PM