我们知道运行一个程序后会进入一个系统调用函数,在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函数返回我们所请求的内存块。
受本人能力,时间精力的限制,分析得比较粗,也难免有出错的地方。只是一个抛砖引玉,有兴趣的朋友可以深入研究。