VC6_SBH 概述
SBH 是 Small Block Heap 的缩写,进行 操作系统之上的小区块内存的治理。在 VC6 中可找到源码实现,降级版本 VC10 中对立应用零碎 API 进行内存申请, SBH 被整合到了操作系统外部。
VC6 局部实现
VC10 局部实现
留神:__heap_init
和 __ioinit
在 VC6 和 VC10 中都存在
VC6_SBH 合成
heap_init(…)
_heap_init(...)
、__sbh_heap_init()
阐明 :
CRT 会先为本人创立一个 _ctrheap “ 独特 ” 内存(HeapCreate), 而后从中配置 SBH 所需的 headers, regions(__sbh_heap_init). 应用程序内存申请时如果 size > threshold 就以 HeapAlloc() 从 _crtheap 取. 若 size <= thread 就从 SBH 取(理论区块来自 VirtualAlloc)
1. 操作系统内存治理的一些概念:可创立一块独特的空间并给其命名,之后相干操作所需的内存都取自这里【逻辑上分类】1.1 HeapCreate 要求操作系统调配一块内存
1.2 __crtheap 全局指针变量,指向调配的内存空间,专为 CRT(C Run Time)应用
2. __sbh_heap_init,到 __ctrheap 指向的内存获取 16 * sizeof(Header) 空间
2.1 每个 SBH 中还有 16 个 HEADER
header 构造剖析
1. 在 _sbh_heap_init() 中含有对 Header 的配置,包含对所属的 region 的配置
2. pHeader 指向理论所治理的可调配的内存区块
3. pRegin 指向治理 Region 区块,其中存储了所治理内存的管制信息
_heap_init() 和 __sbh_heap_init() 调用实现之后就能够进行下一步 _ioinit(), 应用 SBH 对小区块的内存申请进行响应和调配了
_ioinit()
_ioinit() 实现了应用程序启动后的“第一次”内存申请
_malloc_dbg()
示例中依照 dbg 模式进行
_heap_alloc_dbg(nSize)
首先理解 SBH 对所调配的内存块的结构设计,可分为三局部:
_CrtMemBlockHeader、data[nDateSize]、anotherGap[nNoMansLandSize]。
由此能够晓得当应用程序申请 data[nSize] 理论耗费的内存空间大小为 blockSize = sizeof(_CrtMemBlickHeader) + nSize + nNoMansLandSize;
由此又可知 _heap_alloc_dbg(nSize) 的次要工作,调整申请的内存大小,减少 DebugHeader,并进行值填充和初始化。
1. pBlockHeaderNext, 指向链表后内存块
2. pBlockHeaderPre, 指向链表前内存块
3. szFileName, 记录申请应用该内存空间所在的文件
4. nLine, 记录申请应用该内存空间所在文件的行号
5. nDataSize, 理论数据区的空间大小(应用程序理论应用到的内存块)
6. nBlockUse, 示意以后内存块的类型, 如 _CRT_BLOCK、_NORMAL_BLOCK 等...
7. IRequest, 操作流水号
8. gap[nNoMansLandSize],高低两处“栏杆”,爱护应用程序理论应用到的内存,到产生内存越界的状况,调试器能够查看到
1. 上图次要对调配的空间进行数据填充和值初始化,以后续对内存块治理
2. 在调试模式下,malloc 调配的内存都被治理在链表中(及时已被客户应用),因而调试器能够实现简单的性能
3. _pFirstBlock、_pLastBlock 全局指针
_heap_alloc_base(size)
阐明 :
实现对理论内存的调配和治理
1. 当应用程序进行内存申请时,内存相干底层实现会依据申请空间的大小抉择不同的接口
1.1 当申请量小于等于 1016Bytes 时调用 __sbh_alloc_block
1.2 当申请量大于 1016Bytes 时调用操作系统提供的 API HeapAlloc() 从 __crtheap(全局变量) 中取内存
2. __sbh_threshold = 1016 = 1024 - (2 * 4). 其中 2 * 4Bytes 对应两个 cookies 的开销
_sbh_alloc_block(…)
1. (应用程序理论申请量 + DebugHeader + cookie * 2) 向上调整 16 字节【相似于 std::alooc 中的 ROUNDUP()】2. 0x100 + 0x24 + 4 * 2 = 0x12C = 130; 因为是 16 的倍数,所以高低 cookie 最初四位都为 0,借用最初 1 bit 来标记以后内存是否闲暇(1 用户应用,0 闲暇)
__sbh_alloc_new_region()
之前的_ioinit()外部调用细节,次要实现 理论分配内存大小的调整,自此函数开始真正的内存治理动作(能够看到真正的内存治理构造)
1. __sbh_heap_init 创立的 16 个 header, 每个 header 负责管理 1M 字节内存(调用零碎 virtual alloc 取得),将来应用程序的小区块内存申请都从这里切割(管理中心老本 16k 字节)。2. bitvGroupHi, bitvGroupLow 对应 32 *(32 * 2)= 32 * 64bit 的空间,对应 32 个 Group 中的 64 个双向循环链表,记录其中每个链表是否已有闲暇节点(1 示意有,0 示意无)3. listHeader[64] 示意每个 Group 中的 64 个双向循环链表
__sbh_alloc_new_group()
1. 一个 Header 对应治理 1MB 内存,其被逻辑分为 32 块(1MB / 32 = 32kB) 对应到 32 个 Group
2. 每块内存又被分为 8 page(32KB / 8 = 4KB)3. 每个 page 被组织到 Group 的最初一条链表中
4. 当有内存申请时,便从此链表中进行切割提供。当 Group0 再无内存可用,Group 1 便再去治理对应逻辑分块的第二块内存进行调配
Group 中 listHead 治理规定:
listHead[0] 治理 16 字节内存块,顺次叠加 16 字节增长至 listHead[63](1024)。其中所有大于等于 1KB 的内存块都被 listHead[63] 治理
1. 上图黄色标注 0xffffffff (-1) 可了解为“阻隔器”,在内存回收合并时应用,保障相邻的 page 总是被拆散的(不会融合合并)2. 上图红色标注三个区块是 ENTRY 构造,4080 对应 sizeFont(示意闲暇字节数), 是阻隔器之间空间大小
3. 已知 1page 是 4096 字节, 减去两个”阻隔器“4096 -(2 * 4)= 4088,预留(节约)8 字节以进行 16 对齐 4088 - 8 = 4080
4. 在上图 Group 构造中,只有 64 对指针,并没有看到 sizeFont。这因为 Group 只是自在链表的辅助节点,并未对应理论的 page 空间,因而为了节俭内存 sizeFont 借用了上一个 Entry 的 pEntryPrev
-----------------
EntryNext
EntryPrev | sizefont
-----
EntryNext
EntryPrev
-----------------
通过以上步骤,SBH 曾经将肯定数量的内存空间纳入本人的治理之下,并可能按照本人的规定向应用程序提供内存
函数调用栈返回,返回应用程序申请失去的内存地址
1. ioint 进行了第一次 0x100 Bytes 的内存申请
2. 通过调整为 0x130 Bytes [(应用程序理论申请量 + DebugHeader + cookie * 2) 向上调整 16 字节对齐]
3. 调整 sizeFont 为 0xEC0 (0xFF0 - 0x130 = 0xECO)
--
4. 切割 0x130 大小内存,填充高低 Cookie 空间为 0x131 (130 - > 131 后面已解释)
5. 返回申请失去的内存空间(不蕴含 Cookie 地址)6. 填充结构 DebugHeader
7. 返回用用程序理论申请大小的内存空间(不好含 DebugHeader)