《Windows核心編程》---堆管理函數

Windows的“堆”分爲默認堆和私有堆兩種。默認堆是在程序初始化時由操作系統自動創建的,所有標準內存管理函數都是在默認堆中申請內存的;而私有堆相當於在默認堆中保留了一大塊內存,用堆管理函數可以在這個保留的內存區域中分配內存。一個進程的默認堆只有一個,而私有堆可以被創建多個。

 

默認堆可以直接被使用,而私有堆在使用前需要先創建,使用私有堆有很多好處:

1)可以使用默認堆的函數有多種,而它們可能在不同的線程中同時對默認堆進行操作,爲了保持同步,對默認堆的訪問是順序進行的;而私有堆的空間是預留的,不同線程在不同的私有堆中同時分配內存並不會引起衝突,所以整體的運行速度更快。

2)當系統必須在物理內存和頁文件之間進行頁面交換時,系統的性能會受到很大的影響,在某些情況下,使用私有堆可以防止系統頻繁地在物理內存和交換文件之間進行數據交換,因爲將經常訪問的內存侷限在一個小範圍地址的話,頁面交換就不大可能發生,把頻繁訪問的大量小塊內存放在同一個私有堆中就可以保證它們在內存中的位置更近。

3)使用私有堆有利於封裝和保護模塊化的程序。當程序包含多個模塊時,如果使用標準內存管理函數在默認堆中分配內存,那麼所有模塊分配的內存塊是交叉排列在一起的,如果模塊A中的一個錯誤導致內存操作越界,可能會覆蓋模塊B使用的內存塊,到模塊B執行時出錯,我們將很難發現錯誤的源頭來自於模塊A。而如果讓不同模塊使用自己的私有堆,那麼它們的內存就會完全隔離開了,越界錯誤就很容易跟蹤和定位了。

4)使用私有堆使得大量內存的清理變得方便,在默認堆中分配的內存需要一塊塊單獨釋放,但將一個私有堆釋放後,在這個堆裏的內存就全部被釋放掉了。並不需要預先釋放堆棧的每個內存塊。

 

私有堆的創建和釋放:

創建私有堆的函數是HeapCreate:

HANDLE WINAPI HeapCreate(

  __in  DWORD flOptions, //指定堆的屬性:

//HEAP_GENERATE_EXCEPTIONS---指定函數失敗時返回值,不指定這個標誌時,函數失敗

//時返回NULL、否則返回一個具體的錯誤代碼

         //HEAP_NO_SERIALIZE---控制對私有堆的訪問是否要進行獨佔性的檢測;指定這個標誌時,

                            //建立堆時不進行獨佔性檢測,訪問速度可以更快

  __in  SIZE_T dwInitialSize, //創建堆時分配給堆的物理內存(堆的內存不足時可以自動擴展)

  __in  SIZE_T dwMaximumSize //能夠擴展到的最大物理內存

);

 

如果一個堆不再需要了,可以調用HeapDestroy函數將它釋放:

BOOL WINAPI HeapDestroy(

  __in  HANDLE hHeap //堆句柄

);

釋放私有堆可以釋放堆中包含的所有內存塊,也可以將堆佔用的物理內存和保留的地址空間全部返還給系統。如果函數運行成功,返回值是TRUE;當在進程終止時沒有調用HeapDestry函數將私有堆釋放時,系統會自動釋放。

 

 

在堆中分配和釋放內存塊:

如果要在堆中分配內存塊,可以使用HeapAlloc函數:

LPVOID WINAPI HeapAlloc(

  __in  HANDLE hHeap, //堆句柄

  __in  DWORD dwFlags, //以下標識的組合:HEAP_NO_SERIALIZE HEAP_GENERATE_EXCEPTIONS

                                     //HEAP_ZERO_MEMORY

  __in  SIZE_T dwBytes //需要分配的內存塊的字節數

);

注意,在堆中分配的內存塊只能是固定的內存塊,不想GlobalAlloc函數一樣可以分配可移動的內存塊。

 

如果要釋放分配到的內存塊,可以使用HeapFree函數:

BOOL WINAPI HeapFree(

  __in  HANDLE hHeap, //堆句柄

  __in  DWORD dwFlags,

  __in  LPVOID lpMem //要釋放的內存塊指針,由HeapAlloc或HeapReAlloc返回

);

 

對於用HeapAlloc分配的內存塊,可以使用HeapReAlloc函數重新調整大小:

LPVOID WINAPI HeapReAlloc(

  __in  HANDLE hHeap, //堆句柄

  __in  DWORD dwFlags, //以下標識的組合:HEAP_NO_SERIALIZE | HEAP_GENERATE_EXCEPTIONS

                                     //HEAP_ZERO_MEMORY | HEAP_REALLOC_IN_PLACE_ONLY

  __in  LPVOID lpMem, //內存塊指針

  __in  SIZE_T dwBytes //新的大小

);

 

其他堆管理函數:

1)GetProcessHeaps函數用來列出進程中所有的堆:

DWORD WINAPI GetProcessHeaps(

  __in   DWORD NumberOfHeaps, //緩衝區中可以存放句柄的數量

  __out  PHANDLE ProcessHeaps //指向用來接收堆句柄的緩衝區的指針,緩衝區的長度應該等於

                                               //NumberOfHeaps*4字節

);

函數執行後,進程中所有堆的句柄全部返回到緩衝區中,其中也包括默認堆的句柄。

 

GetProcessHeap函數用來獲得進程默認堆的句柄:

HANDLE WINAPI GetProcessHeap(void);

 

 

2)HeapWalk函數用來列出一個堆中所有內存塊:

BOOL WINAPI HeapWalk(

  __in     HANDLE hHeap, //堆句柄

  __inout  LPPROCESS_HEAP_ENTRY lpEntry //指向一個包含PROCESS_HEAP_ENTRY結構的緩衝區

);

調用HeapWalk函數時,函數每次在PROCESS_HEAP_ENTRY結構中返回一個內存塊的信息,如果還有其他內存塊,函數返回TRUE,程序可以一直循環調用HeapWalk函數直到函數返回FALSE爲止。在多線程程序中使用HeapWalk,必須首先使用HeapLock函數將堆鎖定,否則調用會失敗。

 

其中PROCESS_HEAP_ENTRY結構如下,它包含了堆元素的信息:

typedef struct _PROCESS_HEAP_ENTRY {

  PVOID lpData; //指向堆元素數據區的指針,初始調用HeapWalk時,應將lpData設爲NULL

  DWORD cbData; //堆元素數據區的字節大小

  BYTE  cbOverhead; //

  BYTE  iRegionIndex; //

  WORD  wFlags; //

  union {

    struct {

      HANDLE hMem; //

      DWORD  dwReserved[3]; //

    } Block;

    struct {

      DWORD  dwCommittedSize; //

      DWORD  dwUnCommittedSize; //

      LPVOID lpFirstBlock; //

      LPVOID lpLastBlock; //

    } Region;

  } ;

} PROCESS_HEAP_ENTRY, *LPPROCESS_HEAP_ENTRY;

 

3)HeapValidate函數用來驗證堆的完整性或堆中某個內存塊的完整性:

BOOL WINAPI HeapValidate(

  __in      HANDLE hHeap, //要驗證的堆句柄

  __in      DWORD dwFlags, //標誌位

  __in_opt  LPCVOID lpMem //爲NULL,則函數順序驗證堆中所有的內存塊;

                                     //指定一個內存塊,則只驗證這個內存塊

);

如果驗證結果是內存塊都完好無損,函數返回非0值,否則函數返回0。

 

4)HeapLock和HeapUnlock函數用來鎖定和解鎖堆,這兩個函數用於線程的同步:

BOOL WINAPI HeapLock(

  __in  HANDLE hHeap

);

BOOL WINAPI HeapUnlock(

  __in  HANDLE hHeap

);

如果函數執行成功,返回值是非0值,否則返回0。一般來說,很少在程序中使用這兩個函數,而總是使用HEAP_NO_SERIALIZE標誌來進行同步控制,指定了這個標誌的話,HeapAlloc、HeapReAlloc、HeapSize和HeapFree等函數會在內部自己調用HeapLock和HeapUnlock函數。

 

5)HeapSize函數返回堆中某個內存塊的大小,這個大小就是使用HeapAlloc以及HeapReAlloc時指定的大小:

SIZE_T WINAPI HeapSize(

  __in  HANDLE hHeap, //堆句柄

  __in  DWORD dwFlags, //標誌位

  __in  LPCVOID lpMem //需要返回大小的內存塊

);

 

6)HeapCompact函數用於合併堆中的空閒內存塊並釋放不在使用中的內存頁面:

SIZE_T WINAPI HeapCompact(

  __in  HANDLE hHeap,

  __in  DWORD dwFlags

);

 

下面實例代碼使用HeapWalk函數來遍歷一個堆:

 

#include <windows.h>

#include <tchar.h>

#include <stdio.h>

 

int __cdecl _tmain()

{

    DWORD LastError;

    HANDLE hHeap;

    PROCESS_HEAP_ENTRY Entry;

 

    //

    // Create a new heap with default parameters.

    //

    hHeap = HeapCreate(0, 0, 0);

    if (hHeap == NULL) {

        _tprintf(TEXT("Failed to create a new heap with LastError %d./n"),

                 GetLastError());

        return 1;

    }

 

    //

    // Lock the heap to prevent other threads from accessing the heap

    // during enumeration.

    //

    if (HeapLock(hHeap) == FALSE) {

        _tprintf(TEXT("Failed to lock heap with LastError %d./n"),

                 GetLastError());

        return 1;

    }

 

    _tprintf(TEXT("Walking heap %#p.../n/n"), hHeap);

 

    Entry.lpData = NULL;

    while (HeapWalk(hHeap, &Entry) != FALSE) {

        if ((Entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) {

            _tprintf(TEXT("Allocated block"));

 

            if ((Entry.wFlags & PROCESS_HEAP_ENTRY_MOVEABLE) != 0) {

                _tprintf(TEXT(", movable with HANDLE %#p"), Entry.Block.hMem);

            }

 

            if ((Entry.wFlags & PROCESS_HEAP_ENTRY_DDESHARE) != 0) {

                _tprintf(TEXT(", DDESHARE"));

            }

        }

        else if ((Entry.wFlags & PROCESS_HEAP_REGION) != 0) {

            _tprintf(TEXT("Region/n  %d bytes committed/n") /

                     TEXT("  %d bytes uncommitted/n  First block address: %#p/n") /

                     TEXT("  Last block address: %#p/n"),

                     Entry.Region.dwCommittedSize,

                     Entry.Region.dwUnCommittedSize,

                     Entry.Region.lpFirstBlock,

                     Entry.Region.lpLastBlock);

        }

        else if ((Entry.wFlags & PROCESS_HEAP_UNCOMMITTED_RANGE) != 0) {

            _tprintf(TEXT("Uncommitted range/n"));

        }

        else {

            _tprintf(TEXT("Block/n"));

        }

 

        _tprintf(TEXT("  Data portion begins at: %#p/n  Size: %d bytes/n") /

                 TEXT("  Overhead: %d bytes/n  Region index: %d/n/n"),

                 Entry.lpData,

                 Entry.cbData,

                 Entry.cbOverhead,

                 Entry.iRegionIndex);

    }

    LastError = GetLastError();

    if (LastError != ERROR_NO_MORE_ITEMS) {

        _tprintf(TEXT("HeapWalk failed with LastError %d./n"), LastError);

    }

 

    //

    // Unlock the heap to allow other threads to access the heap after

    // enumeration has completed.

    //

    if (HeapUnlock(hHeap) == FALSE) {

        _tprintf(TEXT("Failed to unlock heap with LastError %d./n"),

                 GetLastError());

    }

 

    //

    // When a process terminates, allocated memory is reclaimed by the operating

    // system so it is not really necessary to call HeapDestroy in this example.

    // However, it may be advisable to call HeapDestroy in a longer running

    // application.

    //

    if (HeapDestroy(hHeap) == FALSE) {

        _tprintf(TEXT("Failed to destroy heap with LastError %d./n"),

                 GetLastError());

    }

 

    return 0;

}

 

下面的實例代碼使用GetProcessHeaps函數獲得進程中所有堆的句柄:

#include <windows.h>

#include <tchar.h>

#include <stdio.h>

#include <intsafe.h>

 

int __cdecl _tmain()

{

    DWORD NumberOfHeaps;

    DWORD HeapsIndex;

    DWORD HeapsLength;

    HANDLE hDefaultProcessHeap;

    HRESULT Result;

    PHANDLE aHeaps;

    SIZE_T BytesToAllocate;

 

    //

    // Retrieve the number of active heaps for the current process

    // so we can calculate the buffer size needed for the heap handles.

    //

    NumberOfHeaps = GetProcessHeaps(0, NULL);

    if (NumberOfHeaps == 0) {

        _tprintf(TEXT("Failed to retrieve the number of heaps with LastError %d./n"),

                 GetLastError());

        return 1;

    }

 

    //

    // Calculate the buffer size.

    //

    Result = SIZETMult(NumberOfHeaps, sizeof(*aHeaps), &BytesToAllocate);

    if (Result != S_OK) {

        _tprintf(TEXT("SIZETMult failed with HR %d./n"), Result);

        return 1;

    }

 

    //

    // Get a handle to the default process heap.

    //

    hDefaultProcessHeap = GetProcessHeap();

    if (hDefaultProcessHeap == NULL) {

        _tprintf(TEXT("Failed to retrieve the default process heap with LastError %d./n"),

                 GetLastError());

        return 1;

    }

 

    //

    // Allocate the buffer from the default process heap.

    //

    aHeaps = (PHANDLE)HeapAlloc(hDefaultProcessHeap, 0, BytesToAllocate);

    if (aHeaps == NULL) {

        _tprintf(TEXT("HeapAlloc failed to allocate %d bytes./n"),

                 BytesToAllocate);

        return 1;

    }

 

    //

    // Save the original number of heaps because we are going to compare it

    // to the return value of the next GetProcessHeaps call.

    //

    HeapsLength = NumberOfHeaps;

 

    //

    // Retrieve handles to the process heaps and print them to stdout.

    // Note that heap functions should be called only on the default heap of the process

    // or on private heaps that your component creates by calling HeapCreate.

    //

    NumberOfHeaps = GetProcessHeaps(HeapsLength, aHeaps);

    if (NumberOfHeaps == 0) {

        _tprintf(TEXT("Failed to retrieve heaps with LastError %d./n"),

                 GetLastError());

        return 1;

    }

    else if (NumberOfHeaps > HeapsLength) {

 

        //

        // Compare the latest number of heaps with the original number of heaps.

        // If the latest number is larger than the original number, another

        // component has created a new heap and the buffer is too small.

        //

        _tprintf(TEXT("Another component created a heap between calls. ") /

                 TEXT("Please try again./n"));

        return 1;

    }

 

    _tprintf(TEXT("Process has %d heaps./n"), HeapsLength);

    for (HeapsIndex = 0; HeapsIndex < HeapsLength; ++HeapsIndex) {

        _tprintf(TEXT("Heap %d at address: %#p./n"),

                 HeapsIndex,

                 aHeaps[HeapsIndex]);

    }

 

    //

    // Release memory allocated from default process heap.

    //

    if (HeapFree(hDefaultProcessHeap, 0, aHeaps) == FALSE) {

        _tprintf(TEXT("Failed to free allocation from default process heap./n"));

    }

 

    return 0;

}

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/ACE1985/archive/2010/07/26/5764917.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章