从Entry Point到main函数调用(3):_heap_init
_heap_init在(1)中提到过该函数用于分配一个堆。这个堆是动态创建的私有堆,与系统为进程分配的默认堆不同。进程启动时,系统会在进程虚拟地址空间中创建一个堆,即为进程的默认堆。默认堆的创建和回收均由系统来完成。除了默认堆,进程中还可以存在若干个私有堆。私有堆可以由进程动态创建,并且在此基础上进行内存分配、释放等操作。
_heap_init函数本质上是调用了HeapCreate函数,HeapCreate也是kernel32.dll提供的API。在CRT0.c 中对_heap_init 的调用为:
#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 */ 这里的_MT表示是否使用运行时库(CRT)的多线程静态版本:(测试环境VC6)
[*]如果在VS中创建一个MFC APP,会加上 _MD 编译选项,表示 _MT 和 _DLL 均被定义,app使用运行时库的多线程版本
[*]如果在VS中创建一个Console APP,会加上_ML 编译选项,app使用单线程静态版的运行时库
这里以普通的CUI程序为例,不会定义_MT,所以采用的堆的初始化方式是_heap_init(0)。来看一下_heap_init 的源代码:
int __cdecl _heap_init (int mtflag){ //Initialize the "big-block" heap first. if ( (_crtheap = HeapCreate( mtflag ? 0 : HEAP_NO_SERIALIZE, BYTES_PER_PAGE, 0 )) == NULL ) return 0; // Pick a heap, any heap __active_heap = __heap_select(); if ( __active_heap == __V6_HEAP ) { //Initialize the small-block heap if (__sbh_heap_init(MAX_ALLOC_DATA_SIZE) == 0) { HeapDestroy(_crtheap); return 0; } } else if ( __active_heap == __V5_HEAP ) { if ( __old_sbh_new_region() == NULL ) { HeapDestroy( _crtheap ); return 0; } } return 1;}在本例中,相当于执行了:
int __cdecl _heap_init (int mtflag){ //Initialize the "big-block" heap first. if ( (_crtheap = HeapCreate(HEAP_NO_SERIALIZE, BYTES_PER_PAGE, 0 )) == NULL ) return 0; // Pick a heap, any heap __active_heap = __heap_select(); return 1;} 代码中第四行的_crtheap 是定义在 heapinit.c 开头处的一个全局变量。它表示当 HeapCreate 运行成功之后,返回一个指向新分配堆的句柄。HeapCreate 函数原型如下:
HANDLE WINAPI HeapCreate(__inDWORD flOptions,__inSIZE_T dwInitialSize,__inSIZE_T dwMaximumSize);第一个参数flOptions 是 HEAP_NO_SERIALIZE。HEAP_NO_SERIALIZE 表示可以对堆采用非序列化的访问。采用序列化的访问可以避免在多个线程在同一块堆上分配内存时产生冲突,这有点儿加上一把同步锁的意思。但如果指定了HEAP_NO_SERIALIZE ,则这种串行访问的方式就被去除掉了。也就是说,不同的线程可以同时用 Handle 去操作同一个堆,这显然容易导致堆内存的破坏。
<div class="quote_title"> 摘自msdn
页:
[1]