从Entry Point到main函数调用(3):_heap_init

转自 http://driftcloudy.iteye.com/blog/1050271

 

_heap_init

在(1)中提到过该函数用于分配一个堆。这个堆是动态创建的私有堆,与系统为进程分配的默认堆不同。进程启动时,系统会在进程虚拟地址空间中创建一个堆,即为进程的默认堆。默认堆的创建和回收均由系统来完成。除了默认堆,进程中还可以存在若干个私有堆。私有堆可以由进程动态创建,并且在此基础上进行内存分配、释放等操作。

 

_heap_init函数本质上是调用了HeapCreate函数,HeapCreate也是kernel32.dll提供的API。在CRT0.c 中对_heap_init 的调用为:

C代码 复制代码  收藏代码
  1. #ifdef _MT   
  2.         if ( !_heap_init(1) )               /* initialize heap */  
  3. #else  /* _MT */   
  4.         if ( !_heap_init(0) )               /* initialize heap */  
  5. #endif  /* _MT */   
  6.             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 的源代码:

C代码 复制代码  收藏代码
  1. int __cdecl _heap_init (int mtflag)   
  2. {   
  3.         //  Initialize the "big-block" heap first.  
  4.         if ( (_crtheap = HeapCreate( mtflag ? 0 : HEAP_NO_SERIALIZE, BYTES_PER_PAGE, 0 )) == NULL )   
  5.             return 0;   
  6.   
  7.         // Pick a heap, any heap   
  8.         __active_heap = __heap_select();   
  9.   
  10.         if ( __active_heap == __V6_HEAP )   
  11.         {   
  12.             //  Initialize the small-block heap  
  13.             if (__sbh_heap_init(MAX_ALLOC_DATA_SIZE) == 0)   
  14.             {   
  15.                 HeapDestroy(_crtheap);   
  16.                 return 0;   
  17.             }   
  18.         }   
  19.         else if ( __active_heap == __V5_HEAP )   
  20.         {   
  21.             if ( __old_sbh_new_region() == NULL )   
  22.             {   
  23.                 HeapDestroy( _crtheap );   
  24.                 return 0;   
  25.             }   
  26.         }   
  27.   
  28.         return 1;   
  29. }  

在本例中,相当于执行了:

C代码 复制代码  收藏代码
  1. int __cdecl _heap_init (int mtflag)   
  2. {   
  3.         //  Initialize the "big-block" heap first.  
  4.         if ( (_crtheap = HeapCreate(  HEAP_NO_SERIALIZE, BYTES_PER_PAGE, 0 )) == NULL )   
  5.             return 0;   
  6.   
  7.         // Pick a heap, any heap   
  8.         __active_heap = __heap_select();   
  9.   
  10.         return 1;   
  11. }  

 

代码中第四行的_crtheap 是定义在 heapinit.c 开头处的一个全局变量。它表示当 HeapCreate 运行成功之后,返回一个指向新分配堆的句柄。HeapCreate 函数原型如下:

C代码 复制代码  收藏代码
  1. HANDLE WINAPI HeapCreate(   
  2.   __in  DWORD flOptions,   
  3.   __in  SIZE_T dwInitialSize,   
  4.   __in  SIZE_T dwMaximumSize   
  5. );  

第一个参数flOptions 是 HEAP_NO_SERIALIZE。HEAP_NO_SERIALIZE 表示可以对堆采用非序列化的访问。采用序列化的访问可以避免在多个线程在同一块堆上分配内存时产生冲突,这有点儿加上一把同步锁的意思。但如果指定了HEAP_NO_SERIALIZE ,则这种串行访问的方式就被去除掉了。也就是说,不同的线程可以同时用 Handle 去操作同一个堆,这显然容易导致堆内存的破坏。

摘自msdn
HEAP_NO_SERIALIZE 可以用于下列情形:
1.进程仅拥有一个线程
2.进程拥有多线程,但仅有一个线程堆指定的堆调用堆函数
3.进程拥有多线程,且应用程序提供他自己的机制来处理指定堆的互斥

 

第二个参数dwInitialSize 指明了堆的初始大小,并且这个值总是要对其到内存中一张page页面的大小。这与PE文件映射进内存中,不同的section之间需要进行对齐是一个道理。通常,win32平台下一张page的大小是0x1000 bytes。

 

WINHEAP.H头文件中定义:

C代码 复制代码  收藏代码
  1. #define BYTES_PER_PARA      16   
  2. #define PARAS_PER_PAGE      256     //  tunable value  
  3. #define BYTES_PER_PAGE      (BYTES_PER_PARA * PARAS_PER_PAGE)  

这样计算出来 16 * 256 = 16^3 =  0x1000 bytes

 

第三个参数dwMaximumSize指明了堆的最大字节数。dwMaximumSize = 0 时,被创建的堆可以在需要的时候被扩建,是一个可增长堆,理论上这个堆的最大大小仅仅受限于可用的内存大小。当dwMaximumSize !=0 时,那么堆的大小是固定的,并且不能大于dwMaximumSize指定的范围。

 

更详细的可以参考:http://msdn.microsoft.com/en-us/library/aa366599%28VS.85%29.aspx

 

 

当HeapCreat 执行完毕后,会接着调用__heap_select 函数。__heap_select 负责为之前创建的堆指定类型,并且存放在全局变量 __active_heap 中,__active_heap 会影响到后续 malloc 时的内存分配方式。

 

WINHEAP.H头文件中定义了三种堆:

C代码 复制代码  收藏代码
  1. //  Heap-selection constants   
  2. #define __SYSTEM_HEAP           1   
  3. #define __V5_HEAP               2   
  4. #define __V6_HEAP               3  

对于WIN2K向上的所有系统,都会将堆指定为 __SYSTEM_HEAP 类型,因为在__heap_select 函数中有如下实现:

C代码 复制代码  收藏代码
  1. OSVERSIONINFO osvi;   
  2.   
  3. // First, check the OS for NT >= 5.0   
  4. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);   
  5.   
  6. if ( GetVersionEx(&osvi) )   
  7.     if ( (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osvi.dwMajorVersion >= 5) )   
  8.         return __SYSTEM_HEAP;  

至于__V5_HEAP、__V6_HEAP暂时不做深究。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值