熱點推薦:
您现在的位置: 電腦知識網 >> 操作系統 >> Windows系統管理 >> 正文

平坦內存空間中的層次結構:Heap和Stack

2022-06-13   來源: Windows系統管理 

《Windows 用戶態程序高效排錯》市場價元 特價元 購買>>   

          本小結主要介紹Heap相關的崩潰和內存洩漏和如何使用pageheap來排錯首先介紹heap的原理不同層面的內存分配接下來通過例子代碼舉例演示heap問題的嚴重性和欺騙性最後介紹如何使用pageheap工具高效地對heap問題排錯

  Heap是對平坦空間的高效管理和利用

       內存是容納代碼和資料的空間無論是stackheap還是DLL都是生長在內存上的代碼的執行效果其實是對內存上的資料進行轉化內存是導致問題最多的地方比如內存不足內存訪問違例內存洩漏等都是常見的問題

       關於內存的詳細信息Programming Applications for Microsoft Windows書中有詳細介紹這裡針對排錯作一些補充

        Windows API中有兩類內存分配函數分別是VirtualAlloc和HeapAlloc前一種是向操作系統申請KB為邊界的整塊內存後者是分配任意大小的內存塊區別在於後者的實現依賴於前者換句話說操作系統管理內存的最小單位是KB這個粒度是固定的(其實根據芯片是可以做調整的這裡只討論最普遍的情況)但用戶的資料不可能恰好都是KB大小KB作單位難免會產生很多浪費解決辦法是依靠用戶態代碼的薄計工作實現比KB單位更小的分配粒度換句話說用戶態的程序需要實現一個Memory Manager通過自身的管理KB為粒度的基礎上提供以字節為粒度的內存分配釋放功能並且能夠平衡好時間利用率和空間利用率

        Windows提供了Heap Manager完成上述功能HeapAlloc函數是Heap Manager的分配函數Heap Manager的工作方式大概是這樣首先分配足夠的大的 KB倍數的連續內存空間然後在這塊內存上開辟一小塊區域用來做薄計接下來把這連續的大塊內存分割成很多尺寸不等的小塊把每一小塊的信息記錄到薄計裡面薄計記錄了每一小塊的起始地址和長度以及是否已經分配

        當內存請求發生的時候HeapManager根據請求的長度在薄計信息裡面找到大小最合適的一塊空間把這塊空間標記成已經分配然後把這塊空間的地址返回這樣就完成了一次內存分配如果找不到長度足夠大的空閒小塊Heap Manager繼續以 KB為粒度向系統申請更多的內存

         當用戶需要釋放內存的時候調用HeapFree同時傳入起始地址HeapManager在薄計信息中找到這塊地址把這塊地址的信息由已經分配改回沒有分配當Heap Manager發現有大量的連續空閒空間的時候也會調用VirtualFree來把這些內存歸還給操作系統在實現上面這些基本功能的情況下 HeapManager還需要考慮到

         分配的內存最好是在字節邊界上這樣可以提高內存訪問效率

         做好線程同步保證多個線程同時分配內存的時候不會出現錯誤

         盡可能節省維護薄計的開銷提高性能避免不必要的計算和檢查所以HeapManager假設用戶的代碼沒有bug比如用戶代碼永遠不會越界對內存塊進行存取這樣就可以省去檢查核對的開銷

         優化內部的內存塊管理比如靈活地合並連續的內存小塊以便滿足長尺寸的內存申請或者拆分連續內存塊提高小尺寸的內存使用率

有了上面的理解後看下面一些情況

         如果首先用HeapAlloc分配了一塊空間然後用HeapFree釋放了這塊空間但是在釋放後繼續對這塊空間做操作程序會發生訪問違例錯誤嗎?答案是不會除非HeapManager恰好把那塊地址用VirtualFree返還給操作系統了但是帶來的結果是什麼?是非預期結果也就是說誰都無法保證最後會產生什麼情況程序可能不會有什麼問題也可能會格式化整個硬盤出現得最多的情況是這塊內存後來被Heap Manager重新分配出去導致兩個本應指向不同地址的指針指向同一個地址伴隨而來的是資料損壞或者訪問違例等等

         如果用HeapAlloc分配了KB的空間但是訪問的長度超過了KB會怎麼樣?如果KB恰好在KB內存邊界上而且恰好後面的內存地址並沒有被映像上來程序不會崩潰的這時越界的寫操作要麼寫到別的內存塊上要麼就寫入薄計信息中破壞了薄計導致的結果是 HeapManager維護的數據損壞導致非預期結果

         其他錯誤的代碼比如對同一個地址HeapFree了兩次多線程訪問的時候忘記在調用HeapAllocate的第二個參數中傳入SERIALIZE bit等等都會導致非預期結果

          總的來說上面這些情況都會導致非預期結果如果問題發生後程序立刻崩潰或者拋出異常則可以在第一時間截獲這個錯誤但是現實的情況是這些錯誤不會有及時的效果錯誤帶來的後果會暫時隱藏起來在程序繼續執行幾個小時後突然在一些看起來絕對不可能出現錯誤的地方崩潰比如在調用HeapAllocate/HeapFree的時候崩潰比如訪問一個剛剛分配好的地址的時候崩潰這個時候哪怕抓到了崩潰的詳細信息也無濟於事因為問題根源潛伏在很久以前這種根源在前現象在後的情況會給調試帶來極大的困難

仔細考慮這種難於調試的情況錯誤之所以沒有在第一時間暴露在於下面兩點

         Heap每一塊內存的界限是Heap Manager定義的而內存訪問無效的界限是操作系統定義的哪怕訪問越界如果越界的地方已經有映像上來的KB為粒度的內存頁程序就不會立刻崩潰

         為了提高效率Heap Manager不會主動檢查自身的數據結構是否被破壞

        所以為了方便檢查Heap上的錯誤讓現象盡早表現出來Heap Manager應該這樣管理內存

         把所有的Heap內存都分配到KB頁的結尾然後把下一個KB頁面標記為不可訪問越界訪問發生時候就會訪問到無效地址程序就立刻崩潰

         每次調用Heap相關函數的時候Heap Manager主動去檢查自身的數據結構是否被破壞如果檢查到這樣的情況就主動報告出來


From:http://tw.wingwit.com/Article/os/xtgl/201311/10150.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.