我们知道运行一个程序后会进入一个系统调用函数,在vc6里我们可以设置这个函数,windows下可能会是wWinMainCRTStartup,那会不会就是在这里调用的?
我们通过查找源代码证实了就是在这里面调用的:
#ifdef _WINMAIN_
#ifdef WPRFLAG
void wWinMainCRTStartup(
#else /* WPRFLAG */
void WinMainCRTStartup(
#endif /* WPRFLAG */
#else /* _WINMAIN_ */
#ifdef WPRFLAG
void wmainCRTStartup(
#else /* WPRFLAG */
void mainCRTStartup(
#endif /* WPRFLAG */
#endif /* _WINMAIN_ */
void
)
{
int mainret;
#ifdef _WINMAIN_
_TUCHAR *lpszCommandLine;
STARTUPINFO StartupInfo;
#endif /* _WINMAIN_ */
/*
* Get the full Win32 version
*/
_osver = GetVersion();
_winminor = (_osver >> 8) & 0x00FF ;
_winmajor = _osver & 0x00FF ;
_winver = (_winmajor << 8) + _winminor;
_osver = (_osver >> 16) & 0x00FFFF ;
#ifdef _MT
if ( !_heap_init(1) ) /* initialize heap */
#else /* _MT */
if ( !_heap_init(0) ) /* initialize heap */
#endif /* _MT */
fast_error_exit(_RT_HEAPINIT); /* write message and die */
#ifdef _MT
if( !_mtinit() ) /* initialize multi-thread */
fast_error_exit(_RT_THREAD); /* write message and die */
#endif /* _MT */
/*
* Guard the remainder of the initialization code and the call
* to user's main, or WinMain, function in a __try/__except
* statement.
*/
__try {
_ioinit(); /* initialize lowio */
#ifdef WPRFLAG
/* get wide cmd line info */
_wcmdln = (wchar_t *)__crtGetCommandLineW();
/* get wide environ info */
_wenvptr = (wchar_t *)__crtGetEnvironmentStringsW();
_wsetargv();
_wsetenvp();
#else /* WPRFLAG */
/* get cmd line info */
_acmdln = (char *)GetCommandLineA();
/* get environ info */
_aenvptr = (char *)__crtGetEnvironmentStringsA();
_setargv();
_setenvp();
#endif /* WPRFLAG */
_cinit(); /* do C data initialize */
#ifdef _WINMAIN_
StartupInfo.dwFlags = 0;
GetStartupInfo( &StartupInfo );
#ifdef WPRFLAG
lpszCommandLine = _wwincmdln();
mainret = wWinMain(
#else /* WPRFLAG */
lpszCommandLine = _wincmdln();
mainret = WinMain(
#endif /* WPRFLAG */
GetModuleHandleA(NULL),
NULL,
lpszCommandLine,
StartupInfo.dwFlags & STARTF_USESHOWWINDOW
? StartupInfo.wShowWindow
: SW_SHOWDEFAULT
);
#else /* _WINMAIN_ */
#ifdef WPRFLAG
__winitenv = _wenviron;
mainret = wmain(__argc, __wargv, _wenviron);
#else /* WPRFLAG */
__initenv = _environ;
mainret = main(__argc, __argv, _environ);
#endif /* WPRFLAG */
#endif /* _WINMAIN_ */
exit(mainret);
}
__except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
{
/*
* Should never reach here
*/
_exit( GetExceptionCode() );
} /* end of try - except */
}
在这里我们可以看到在调用我们熟悉的main,wmain,WinMain函数之前就调用_heap_init初始化了,所以我们在main函数中就可以使用new去分配内存了。
我们再回到函数__sbh_alloc_block,这个函数进行了一些设置,分配内存的地方还得继续深入到__sbh_alloc_new_region中,我们再看看这个函数
PHEADER __cdecl __sbh_alloc_new_region (void)
{
PHEADER pHeader;
// create a new entry in the header list
// if list if full, realloc to extend its size
if (__sbh_cntHeaderList == __sbh_sizeHeaderList)
{
if (!(pHeader = (PHEADER)HeapReAlloc(_crtheap, 0, __sbh_pHeaderList,
(__sbh_sizeHeaderList + 16) * sizeof(HEADER))))
return NULL;
// update pointer and counter values
__sbh_pHeaderList = pHeader;
__sbh_sizeHeaderList += 16;
}
// point to new header in list
pHeader = __sbh_pHeaderList + __sbh_cntHeaderList;
// allocate a new region associated with the new header
if (!(pHeader->pRegion = (PREGION)HeapAlloc(_crtheap, HEAP_ZERO_MEMORY,
sizeof(REGION))))
return NULL;
// reserve address space for heap data in the region
if ((pHeader->pHeapData = VirtualAlloc(0, BYTES_PER_REGION,
MEM_RESERVE, PAGE_READWRITE)) == NULL)
{
HeapFree(_crtheap, 0, pHeader->pRegion);
return NULL;
}
// initialize alloc and commit group vectors
pHeader->bitvEntryHi = 0;
pHeader->bitvEntryLo = 0;
pHeader->bitvCommit = BITV_COMMIT_INIT;
// complete entry by incrementing list count
__sbh_cntHeaderList++;
// initialize index of group to try first (none defined yet)
pHeader->pRegion->indGroupUse = -1;
return pHeader;
}
最后还是调用这个函数HeapAlloc去分配了内存,自此整个内存分配结束。
通过上面的分析查找,整个流程就比较清晰了,以程序入口函数wWinMainCRTStartup为例,我们在执行一个程序后,系统首先调用wWinMainCRTStartup函数,在这个函数里调用_heap_init去初始化操作,接下来调用wWinMain,也就是我们所写的程序入口函数,这时候我们就可以使用new去分配内存了,new函数调用_nh_malloc,再调用_nh_malloc_dbg,接着调用_heap_alloc_dbg,往下是_heap_alloc_base,再然后__sbh_alloc_block,然后调用__sbh_alloc_new_region,通过一层层的添加需要的标志,参数,最后通过调用HeapAlloc函数返回我们所请求的内存块。
受本人能力,时间精力的限制,分析得比较粗,也难免有出错的地方。只是一个抛砖引玉,有兴趣的朋友可以深入研究。