曲径通幽论坛

标题: VirtualAlloc() -- 分配保留或提交的内存页 [打印本页]

作者: beyes    时间: 2012-2-29 14:36
标题: VirtualAlloc() -- 分配保留或提交的内存页
VirtualAlloc() 原型如下:
[C++] 纯文本查看 复制代码
LPVOID WINAPI VirtualAlloc(
  __in_opt  LPVOID lpAddress,
  __in      SIZE_T dwSize,
  __in      DWORD flAllocationType,
  __in      DWORD flProtect
);

该函数在用于分配虚拟内存时,可以指定分配的内存页面是保留的 (Reserved)还是提交的 (Committed)。

第 1 个参数 lpAddress 是输入参数,指定分配的起始位置。如果要保留一段内存区域,那么函数会将其自动向最近的一个分配粒度(一般为 64K)对齐。如果内存已经被保存并且打算将其提交,那么函数将会向最近的一个页面对其。如果设为 NULL ,那么系统将自行决定在什么地方分配。

第 2 个参数 dwSize 是输入参数,指定要分配的内存区域的大小。

第 3 个参数 flAllocationType 是输入参数,表示分配的类型,决定了是保留一段内存区域,还是提交,还是两者同时完成。如果设置为 MEM_COMMIT ,那么将提交内存页面;如果设置为 MEM_RESERVE ,那么将保留页面;如果设置为 MEM_COMMIT | MEM_RESERVE 那么将直接从空闲页面提交为 “已提交的” 页面。

第 4 个参数 flProtect 是输入参数,表示内存的属性,其值可以如下:

PAGE_NOACCESS : 拒绝访问。
PAGE_READONLY : 只读。
PAGE_READWRITE : 可读写。
PAGE_WRITECOPY : 只读 或 写时拷贝(copy-on-write)一个文件映射对象。
PAGE_EXECUTE : 该区域包含可执行代码,拒绝对该区域读写。
PAGE_EXECUTE_READ :该区域包含可执行代码,也可读该区域。
PAGE_EXECUTE_READWRITE : 该区域包含可执行代码,并且可读写。
PAGE_EXECUTE_WRITECOPY :该区域包含可执行代码,只读,或写时拷贝。

函数返回 LPVOID 类型,表示分配到的内存的起始地址。如果返回 NULL 则表示函数执行失败,可用 GetLastError() 获取错误信息。

测试代码
[C++] 纯文本查看 复制代码

// VirtualAlloc.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <Windows.h>
#include <locale.h>

int _tmain(int argc, _TCHAR* argv[])
{
    setlocale (LC_ALL, "CHS");

    SIZE_T    sizeVirtual = 4000;            // 大小
    LPVOID    lpRound = (LPVOID)0x100000FF;   // 地址
    MEMORY_BASIC_INFORMATION mbi;            // 内存信息

    LPVOID    lpAddress = VirtualAlloc(
                                lpRound,
                                sizeVirtual,
                                MEM_COMMIT | MEM_RESERVE,
                                PAGE_READWRITE);
    if (lpAddress == NULL) {
        _tprintf (TEXT("VirtualAlloc error : %d\n"), GetLastError());
        return (-1);
    }
    _tprintf (TEXT("Alloc: MEM_COMMIT | MEM_RESERVE\n"));

    //复制数据到内存中
    CopyMemory (lpAddress, TEXT("Hello Windows"), 2*_tcslen(TEXT("Hello Windows")));

    _tprintf (TEXT("分配与内存复制成功,分配地址: 0x%.8x, 复制内容:%s\n"), lpAddress, lpAddress);


    //获取内存信息并将其打印
    VirtualQuery (lpAddress, &mbi, sizeof(mbi));
    _tprintf (TEXT("\n使用 VirtualQuery() 获得的内存信息:\n"));
    _tprintf (TEXT( "BaseAddress: 0x%.8x\nAllocationBase: 0x%.8x\nAllocationProtect: 0x%.8x\nRegionSize: %u\nState: 0x%.8x\nProtect: 0x%.8x\nType: 0x%.8x\n"),
        mbi.BaseAddress, mbi.AllocationBase, mbi.AllocationProtect,  mbi.RegionSize, mbi.State, mbi.Protect, mbi.Type);

    //DECOMMIT 释放,页面变为保留状态
    _tprintf (TEXT("Free: DECOMMIT\n"));
    if (!VirtualFree(lpRound, sizeVirtual, MEM_DECOMMIT)) {
        _tprintf (TEXT("VirtualFree() Error: %d\n"), GetLastError());
        return (-1);
    }
   
    //再次获取内存信息并打印
    VirtualQuery (lpAddress, &mbi, sizeof(mbi));
    _tprintf (TEXT("\n使用 VirtualQuery() 获得的内存信息:\n"));
    _tprintf (TEXT( "BaseAddress: 0x%.8x\nAllocationBase: 0x%.8x\nAllocationProtect: 0x%.8x\nRegionSize: %u\nState: 0x%.8x\nProtect: 0x%.8x\nType: 0x%.8x\n"),
        mbi.BaseAddress, mbi.AllocationBase, mbi.AllocationProtect,  mbi.RegionSize, mbi.State, mbi.Protect, mbi.Type);

    //最后释放内存
    _tprintf (TEXT("Free : Release\n"));
    if (!VirtualFree(lpAddress, 0, MEM_RELEASE)) {
        _tprintf (TEXT("VirtualFree Error : %d\n"), GetLastError());
        return (-1);
    }

    return 0;
}

运行输出:
D:\WinAPI\VirtualAlloc\Debug>VirtualAlloc.exe
Alloc: MEM_COMMIT | MEM_RESERVE
分配与内存复制成功,分配地址: 0x10000000, 复制内容:Hello Windows

使用 VirtualQuery() 获得的内存信息:
BaseAddress: 0x10000000
AllocationBase: 0x10000000
AllocationProtect: 0x00000004
RegionSize: 8192
State: 0x00001000
Protect: 0x00000004
Type: 0x00020000
Free: DECOMMIT

使用 VirtualQuery() 获得的内存信息:
BaseAddress: 0x10000000
AllocationBase: 0x10000000
AllocationProtect: 0x00000004
RegionSize: 8192
State: 0x00002000
Protect: 0x00000000
Type: 0x00020000
Free : Release
注意输出结果中分配时指定的地址和大小与分配后地址和大小的变化。

在分配时,虽然 VirtualAlloc() 指定的分配地址为 0x100000FF ,大小为 4000 (0xFA0),但在分配完后,VirtualAlloc()  返回的地址是 0x100000000 (通过函数 VirtualQuery() 得知),大小为 8192 。产生这种差异的原因是:
0x100000FF 基地址 + 0xFA0 大小 = 0x1000109F ---> 该地址跨越了两个虚拟内存分页 0x10000000 ~ 0x10001000 。因此 VirtualAlloc() 会自动将分配对齐,这样实际上就是拥有了 2 个内存分页的大小,即 8192 个字节。




欢迎光临 曲径通幽论坛 (http://www.groad.net/bbs/) Powered by Discuz! X3.2