这个测试是否证明malloc,calloc,new在我的系统上管理自己的内存池?

时间:2017-09-05 23:39:29

标签: c++ c winapi memory-management

我正在编写一个测试程序,以了解与使用malloc创建的LPWSTR相比,BSTR的工作原理。

我决定在BSTR和LPWSTR上调用HeapSize来查看它们是否在进程堆上。

BSTR不在进程堆上,但我发现LPWSTR的HeapSize远远大于我复制到其中的Hello World字符串。值非常大,GetLastError返回0表示没有错误。它似乎为我使用malloc创建的每个LPWSTR提供了相同的大,所以我做了一个测试,看它是否只是从同一个c运行时堆中分配的。

以下是上述代码:

#include <stdio.h>
#include <Windows.h>
#include <Windowsx.h>

int main(int argc, char **argv)
{
    void *testalloc1;
    void *testalloc2;
    LPWSTR w;
    BSTR b;

    testalloc1 = malloc(1024);
    testalloc2 = calloc(512, 2);
    w = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, sizeof(L"Hello, World!"));
    wcsncpy(w, L"Hello, World!", sizeof(L"Hello, World!") / sizeof(WCHAR));
    b = SysAllocString(w);

    _putws(w);
    printf("%d.\n", *(DWORD *)(((LONG_PTR)b) - 4));
    _putws(b);

    printf("w -> %d.\n", HeapSize(GetProcessHeap(), 0, w));
    printf("GetLastError -> 0x%d.\n", GetLastError());
    printf("testalloc1 -> %d.\n", HeapSize(GetProcessHeap(), 0, testalloc1));
    printf("GetLastError -> 0x%d.\n", GetLastError());
    printf("testalloc2 -> %d.\n", HeapSize(GetProcessHeap(), 0, testalloc2));
    printf("GetLastError -> 0x%d.\n", GetLastError());
    printf("b -> %d.\n", HeapSize(
        GetProcessHeap(), 
        0, 
        (LPVOID)((LONG_PTR)b - sizeof(LONG_PTR))
    ));
    printf("GetLastError -> 0x%d.\n", GetLastError());

    return 0;
}

导致:

Hello, World!
26.
Hello, World!
w -> 28.
GetLastError -> 0x0.
testalloc1 -> 47120.
GetLastError -> 0x0.
testalloc2 -> 47120.
GetLastError -> 0x0.
b -> 32.
GetLastError -> 0x0.

我写了另一个测试,试着了解发生了什么;我使用不同的堆类分配函数(HeapAlloc,malloc,calloc)分配了多个内存块并得到了这个输出:

malloc(1024) -> 215432.
calloc(512, 2) -> 215432
calloc(2, 512) -> 215432
new BYTE[1024] -> 215432
new BYTE[large] -> 1024.
malloc(large) -> 1024.
HeapAlloc(1024) -> 1024.

这是上面输出的代码:

#include <Windows.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    HANDLE heap;
    void *mem[7];
    LONG_PTR limit;

    heap = GetProcessHeap();    

    mem[0] = malloc(1024);
    mem[1] = calloc(512, 2);
    mem[2] = calloc(2, 512);
    mem[3] = (void *)new BYTE[1024];

    limit = HeapSize(heap, 0, mem[0]);

    mem[4] = (void *)new BYTE[limit];
    mem[5] = malloc(limit);
    mem[6] = HeapAlloc(heap, 0, 1024);

    printf("malloc(1024) -> %d.\n", HeapSize(heap, 0, mem[0]));
    printf("calloc(512, 2) -> %d.\n", HeapSize(heap, 0, mem[1]));
    printf("calloc(2, 512) -> %d.\n", HeapSize(heap, 0, mem[2]));    
    printf("new BYTE[1024] -> %d.\n", HeapSize(heap, 0, mem[3]));
    printf("new BYTE[large] -> %d.\n", HeapSize(heap, 0, mem[4]));
    printf("malloc(large) -> %d.\n", HeapSize(heap, 0, mem[5]));
    printf("HeapAlloc(1024) -> %d.\n", HeapSize(heap, 0, mem[6]));

    return 0;
}

这段代码是否合理地证明malloc,calloc,new都使用内存池作为分配的首选,否则HeapAlloc(在我的系统上),或者我错过了什么?

1 个答案:

答案 0 :(得分:3)

对于某些内存指针的调用HeapSize,我们必须首先知道内存块所在的堆的句柄,然后我们必须确保这个内存指针正好在分配块的开头 - 否则HeapSize生成异常,如果我们在调试器下,并且没有返回正确的值。

但是可能由给定的指针确定 - 它是属于某个堆(在进程中可以是几个堆)而不仅是块大小,而是从块开始的偏移量(比如SysAllocString返回不是指向已分配块的指针,但是从块开始偏移8)。这可以在GetProcessHeapsHeapWalk的帮助下完成(在HeapLockHeapUnlock内)

void TestHeapPointer(PVOID pv)
{
    static volatile UCHAR guz;

    ULONG NumberOfHeaps = 0, n = 8;

    union {
        PVOID buf;
        PHANDLE ProcessHeaps;
    };

    PVOID stack = alloca(guz);

    BOOL bFound = FALSE;

    do 
    {
        NumberOfHeaps = RtlPointerToOffset(buf = alloca((n - NumberOfHeaps) * sizeof(HANDLE)), stack) / sizeof(HANDLE);

        if (NumberOfHeaps >= (n = GetProcessHeaps(NumberOfHeaps, ProcessHeaps)))
        {
            do 
            {
                HANDLE hHeap = *ProcessHeaps++;

                if (HeapLock(hHeap))
                {
                    PROCESS_HEAP_ENTRY phe = {};

                    while (HeapWalk(hHeap, &phe))
                    {
                        if (phe.wFlags & PROCESS_HEAP_ENTRY_BUSY)
                        {
                            ULONG_PTR ofs = (ULONG_PTR)pv - (ULONG_PTR)phe.lpData;
                            if (ofs < phe.cbData)
                            {
                                DbgPrint("%p in %p block %x size ofs=%x\n", pv, phe.lpData, phe.cbData, ofs);
                                bFound = TRUE;
                                break;
                            }
                        }
                    }
                    HeapUnlock(hHeap);
                }

            } while (!bFound && --NumberOfHeaps);

            break;
        }
    } while (NumberOfHeaps < n);

    if (!bFound) DbgPrint("%p not in heaps", pv);
}

和测试一样

static const WCHAR cstr[] =  L"Hello, World!";

if (PVOID pv = malloc(sizeof(cstr)))
{
    TestHeapPointer(pv);
    free(pv);
}

if (BSTR b = SysAllocString(cstr))
{
    TestHeapPointer(b);
    SysFreeString(b);
}