09 年翻译的东西。

原文见:  http://www.nobugs.org/developer/win32/debug_crt_heap.html

在DeviceStudio的Debug编译模式下, crt中的堆内存分配操作----包括malloc()和free()----使用一个特殊的, 便于调试的版本, 我们称之为crt debug堆(译注: 下面简称CDH). 相比于电光火石(译注: 原文blazingly, 我想不出更确切的说法)的运行效率, 调试版本更关注对于堆错误的定位, 它通过以下三种手法实现以上诉求:

1.用守护内存块包围新分配的内存, 这样就可以侦测到缓冲过载和欠载. 所谓守护内存块就是一系列被填充为0xfd的内存字节, 又被称为”无主之地”.

2.用一个特殊的值(0xcd)初始化新申请的内存.

3.同时用一个特殊的值填充(0xdd) 被释放的内存.

为了便于记忆, 可以这样理解以上用于填充的值:

1.0xcd意为Clean Memory.

2.0xdd意为Dead Memory.

3.0xfd意为Fences(译注: 栅栏).

CDH将大部分工作交由win32 api中的堆函数HeapAlloc()和HeapFree()完成, 每进程4Gb的虚地址空间的分块和管理是由kernel32.dl中的Win32堆自己完成的.

当你调用malloc(8)分配8字节的内存时, CDH会调用HeapAlloc()申请48字节的内存, 额外的40字节被用来存放内存块的额外信息----比如调用malloc()的源文件和行号, 以及指向上/下一个内存块的指针.在后面的列表中, 所有的crt调试信息均被标记为红色.

HeaoAlloc()本身也需要记录簿记(译注: 原文bookkeeping)信息, 事实上,一个HeaoAlloc()调用会在进程地址空间里保留80字节内存, 其中8字节的簿记信息出现在真正使用的40字节之前, 剩下的32字节在真正使用的40字节之后.在下面的列表里, Win32堆簿记信息被标记为灰色.最后, HeapAlloc()返回的内存总是被一4字节对齐初始化为0xBAADF00D.顺道说一句: 当你使用经由VirtualAlloc()管理内存的虚拟内存管理器申请内存页时, 获取的内存页会被初始化为0, 所以可以断定HeapAlloc()按照上述模式执行了额外的初始化工作.

crt取得40字节的内存块后会填入自己的簿记信息. 头两个字(译注: 即WORD)用来存放直向”前一个”和”后一个”crt堆内存块的指针. 这里的前后不能从字面去理解, 因为所谓指向”后一个”内存块的指针事实上指向的是时间顺序上紧邻本内存块之前分配的内存块, 相应的, 指向“前一个”的指针指向的是下一个将被分配的内存块. 之所以这样命名, 是因为内存块链表是从最后分配的内存块开始的. 同时, 为了使堆检查代码能遍历每个内存块, CDH还保存着第一块和最后一块内存的地址(_pFirstBlock和_pLastBlock).

如果调用malloc()代码所在的文件名和行号是已知的, 它们将被被保存在第三第四个字中, 紧接着下面一个字表示本块申请了多少字节内存. 再下面一个字是类型域, 等于1表示new或malloc()分配的普通块, 2表示crt分配的供内部使用的块. 0表示已经被用户释放但是还未归还给win32堆的块(详见下文). 通常来说, 新申请的内存块本位置等于1. 最后一块是计数器, 每执行一次内存分配计数器加1.

通过malloc()得到的8字节内存无用内存包围. 这些空内存被填充为0xfd, 当整个内存块被free()时, crt会检查这些空内存存放的值是否仍然是0xfd. 如果值改变了, 说明程序有错误存在. 不幸的是, 错误可能发生的远比检查到的早, 如果需要更精确的检查, 可以使用Purify或者Boundschecker, 他们能让程序停止在内存崩溃的点上, 如果你不想花钱, 你可以等我写篇文章告诉你怎样巧妙的实现这个功能.

真正被使用的8字节内存被初始化为0xcd, 如果你的对象中间出现连续的0xcd, 那么你一定是忘记了初始化一些东西.

当你调用free()释放上述8字节的内存时, crt首先会用0xdddddddd填充全部48字节的内存块(包括簿记信息), 这样就可以通过检查这块内存的值获知这块内存在释放后是否右被写入过(比如使用野指针写内存).

接下来, crt通常会调用HeapFree()函数将本内存块归还给win32堆, win32堆会将本内存块填充为0xFEEEFEEE. 注意, crt并不维护”空闲块列表”, 这些都由HeapFree()来做(译注: 也就是说空闲列表是由win32 堆来维护的).

但是, 你可以让crt不把被释放的内存块归还给win32堆(译注: 也就是不调用HeapFree()), 方法是将_CRTDBG_DELAY_FREE_MEM_DF传递给_CrtSetDbgFlag(), 这在你跟踪野指针错误时将会派上用场,在这种情况下, 内存不会被复用, 所以释放过的内存的值必然是0xDDDDDDDD, 处非你对释放过的内存执行了写操作. 你可以调用_CrtCheckMemory()检查释放过的内存是否被篡改.(译注: 这个函数缺省情况下需要显式调用, 你也可以将_CRTDBG_CHECK_ALWAYS_DF传递给_CrtSetDbgFlag(), 这样每次分配和释放内存时都会调用_CrtCheckMemory())

一个例子:

下面是调用p = malloc(8)然后调用free(p)的过程中内存的变化表, malloc(8)返回的指针为0x00321000, 我列出了偏移后的内存值, 以便你找到你自己分配的内存信息.

Address

Offset

After HeapAlloc()

After malloc()

During free()

After HeapFree()

Comments

0x00320FD8

-40

0x01090009

0x01090009

0x01090009

0x0109005A

Win32 heap info

0x00320FDC

-36

0x01090009

0x00180700

0x01090009

0x00180400

Win32 heap info

0x00320FE0

-32

0xBAADF00D

0x00320798

0xDDDDDDDD

0x00320448

Ptr to next CRT heap block (allocated earlier in time)

0x00320FE4

-28

0xBAADF00D

0x00000000

0xDDDDDDDD

0x00320448

Ptr to prev CRT heap block (allocated later in time)

0x00320FE8

-24

0xBAADF00D

0x00000000

0xDDDDDDDD

0xFEEEFEEE

Filename of malloc() call

0x00320FEC

-20

0xBAADF00D

0x00000000

0xDDDDDDDD

0xFEEEFEEE

Line number of malloc() call

0x00320FF0

-16

0xBAADF00D

0x00000008

0xDDDDDDDD

0xFEEEFEEE

Number of bytes to malloc()

0x00320FF4

-12

0xBAADF00D

0x00000001

0xDDDDDDDD

0xFEEEFEEE

Type (0=Freed, 1=Normal, 2=CRT use, etc)

0x00320FF8

-8

0xBAADF00D

0x00000031

0xDDDDDDDD

0xFEEEFEEE

Request #, increases from 0

0x00320FFC

-4

0xBAADF00D

0xFDFDFDFD

0xDDDDDDDD

0xFEEEFEEE

No mans land

0x00321000

+0

0xBAADF00D

0xCDCDCDCD

0xDDDDDDDD

0xFEEEFEEE

The 8 bytes you wanted

0x00321004

+4

0xBAADF00D

0xCDCDCDCD

0xDDDDDDDD

0xFEEEFEEE

The 8 bytes you wanted

0x00321008

+8

0xBAADF00D

0xFDFDFDFD

0xDDDDDDDD

0xFEEEFEEE

No mans land

0x0032100C

+12

0xBAADF00D

0xBAADF00D

0xDDDDDDDD

0xFEEEFEEE

Win32 heap allocations are rounded up to 16 bytes

0x00321010

+16

0xABABABAB

0xABABABAB

0xABABABAB

0xFEEEFEEE

Win32 heap bookkeeping

0x00321014

+20

0xABABABAB

0xABABABAB

0xABABABAB

0xFEEEFEEE

Win32 heap bookkeeping

0x00321018

+24

0x00000010

0x00000010

0x00000010

0xFEEEFEEE

Win32 heap bookkeeping

0x0032101C

+28

0x00000000

0x00000000

0x00000000

0xFEEEFEEE

Win32 heap bookkeeping

0x00321020

+32

0x00090051

0x00090051

0x00090051

0xFEEEFEEE

Win32 heap bookkeeping

0x00321024

+36

0xFEEE0400

0xFEEE0400

0xFEEE0400

0xFEEEFEEE

Win32 heap bookkeeping

0x00321028

+40

0x00320400

0x00320400

0x00320400

0xFEEEFEEE

Win32 heap bookkeeping

0x0032102C

+44

0x00320400

0x00320400

0x00320400

0xFEEEFEEE

Win32 heap bookkeeping

[老文章搬家] [翻译] 深入解析win32 crt 调试堆的更多相关文章

  1. [老文章搬家] 关于 Huffman 编码

    按:去年接手一个项目,涉及到一个一个叫做Mxpeg的非主流视频编码格式,编解码器是厂商以源代码形式提供的,但是可能代码写的不算健壮,以至于我们tcp直连设备很正常,但是经过一个UDP数据分发服务器之后 ...

  2. 《编译原理》控制流语句 if 和 while 语句的翻译 - 例题解析

    <编译原理>控制流语句 if 和 while 语句的翻译 - 例题解析 将 if 和 while 语句翻译成四元式 注:不同教材会有小差异,使用 _ 或者 - ,如果是 -,请注意区分 - ...

  3. 转:win32下的堆管理系统

    转:https://bbs.pediy.com/thread-224136.htm 准备刷漏洞战争中的堆溢出部分,但是对于堆的了解较少,在这里记录一下关于堆的学习记录,有错误请各位大大拍砖 参考: & ...

  4. VC使用CRT调试功能来检测内存泄漏

    信息来源:csdn     C/C++ 编程语言的最强大功能之一便是其动态分配和释放内存,但是中国有句古话:“最大的长处也可能成为最大的弱点”,那么 C/C++ 应用程序正好印证了这句话.在 C/C+ ...

  5. JavaScript2谁刚开始学习应该知道4最佳实践文章(翻译)

    原版的:24 JavaScript Best Practices for Beginners (注:阅读原文的时候没有注意公布日期,觉得不错就翻译了,翻译到JSON.parse那一节觉得有点不正确路才 ...

  6. Win2D 官方文章系列翻译 - 避免内存泄漏

    本文为个人博客备份文章,原文地址: http://validvoid.net/win2d-avoiding-memory-leaks/ 在托管 XAML 应用中使用 Win2D 控件时,必须谨慎处理对 ...

  7. Win2D 官方文章系列翻译 - 与 Direct2D 互操作

    本文为个人博客备份文章,原文地址: http://validvoid.net/win2d-interop-with-direct2d/ Win2D 作为 Direct2D 的上层实现,支持与其进行双向 ...

  8. Win32 CRT and MFC 清单文件.manifest配制

    Demo.exe.manifest <?xml version="1.0" encoding="UTF-8" standalone="yes&q ...

  9. Spark Streaming + Kafka Integration Guide原文翻译及解析

    前面写了关于kafka和spark streaming的结合使用(https://www.cnblogs.com/qfxydtk/p/11662591.html),其具体使用用法其实来自于原文:htt ...

随机推荐

  1. [总结] Versions crashing in OS X Yosemite (10.10)

    在文本编辑器中打开 ~/.subversion/servers 在 [global] 下添加该行: http-library = serf 然后,安装 Versions 1.3.2

  2. SCNU 2015ACM新生赛决赛【F. Oyk闯机关】解题报告

            题目大意:一个$N$$\times$$N$的阵列,每个格子有$X_{ij}$个调和之音,若每次只能选择走右边或下边,从左上角出发走到右下角,问最多能收集到多少个调和之音?       ...

  3. HDOJ 1008. Elevator 简单模拟水题

    Elevator Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Su ...

  4. sql语句返回值的问题

    由于执行sql语句的时候执行成功或者失败会返回执行的影响函数,用list是因为查询的结果可能为null也可能set后放到集合里去: 所以返回值类型用int

  5. svn cleanup 执行失败时,可以勾选 break locks,

  6. [LeetCode] Insert Interval 插入区间

    Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessa ...

  7. Hello session

    1. session 随想 HTTP 的无状态,也就是说,每次请求都是独立的线程.这里所说的无状态其实就是一种隔离的意思.举个例子比如购物车,你先选择A商品,加入购物车,这里就是A线程,然后在选择B商 ...

  8. PHP SPL(PHP 标准库)

    一.什么是SPL? SPL是用于解决典型问题(standard problems)的一组接口与类的集合.(出自:http://php.net/manual/zh/intro.spl.php) SPL, ...

  9. js中的json对象和字符串之间的转化

    字符串转对象(strJSON代表json字符串)   var obj = eval(strJSON);   var obj = strJSON.parseJSON();   var obj = JSO ...

  10. Xftp连接linux(ubuntu)时提示ssh服务器拒绝了密码,请再试一次

    用xftp root连接时显示ssh服务器拒绝了密码,请重新连接.由于sshd的设置不允许root用户用密码远程登录,修改/etc/ssh/sshd_config文件,但必须是安装了openssh才会 ...