[老文章搬家] [翻譯] 深刻解析win32 crt 調試堆

09 年翻譯的東西。html

 

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

在DeviceStudio的Debug編譯模式下, crt中的堆內存分配操做----包括malloc()和free()----使用一個特殊的, 便於調試的版本, 咱們稱之爲crt debug堆(譯註: 下面簡稱CDH). 相比於電光火石(譯註: 原文blazingly, 我想不出更確切的說法)的運行效率, 調試版本更關注對於堆錯誤的定位, 它經過如下三種手法實現以上訴求:api

1.用守護內存塊包圍新分配的內存, 這樣就能夠偵測到緩衝過載和欠載. 所謂守護內存塊就是一系列被填充爲0xfd的內存字節, 又被稱爲」無主之地」.函數

2.用一個特殊的值(0xcd)初始化新申請的內存.翻譯

3.同時用一個特殊的值填充(0xdd) 被釋放的內存.debug

 

爲了便於記憶, 能夠這樣理解以上用於填充的值:指針

1.0xcd意爲Clean Memory.調試

2.0xdd意爲Dead Memory.orm

3.0xfd意爲Fences(譯註: 柵欄).htm

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

相關文章
相關標籤/搜索