• 《Windows核心编程》学习笔记(12)– 虚拟内存


    当系统创建一个进程并赋予地址空间时,可用地址空间中的大部分是闲置的或尚未分配的。为了使用这部分地址空间,我们必须调用VirtualAlloc函数来分配其中的区域。分配区域的操作被称为预定

    当应用程序预定地址空间区域时,系统会确保区域的起始地址正好是分配粒度的整数倍分配粒度会根据不同的CPU平台而有所不同,但是到目前为止,所有的CPU平台都使用相同的分配粒度,大小为64KB。也就是说系统会把分配请求取整到64KB的整数倍。

    当应用程序预定地址空间的一块区域时,系统会确保区域的大小正好是系统页面大小的整数倍。页面是一个内存单元,系统通过它来管理内存。与分配粒度相似,页面大小会根据不同CPU而有所不同。x86 x64系统使用页面大小为4KB,IA-64系统使用页面大小为8KB。例如,应用程序试图预防一块大小为10KB的地址空间区域,那么系统会自动将该请求取整到页面大小的整数倍,然后用取整后的大小预定区域。这意味着在x86 x64 系统中,系统会预定一块大小为 12 KB 的区域。

    当程序不再访问所预定的空间区域时,应该释放该区域。这个过程被称为释放地址空间区域。通过调用VitualFree函数来完成。

     

    为了让32位的程序能在64位版本的Windows运行,微软提供了一个Windows 32-bit On Windows 64-bit的模拟层,又称WOW64

    判断程序是否在WOW64运行:

    BOOL WINAPI IsWow64Process(

      __in          HANDLE hProcess,

      __out         PBOOL Wow64Process

    );

    只有当32位应用程序在WOW64上运行时参数Wow64Process才会被设为TRUE

     

    在这种情况下,我们需要调用GetNativeSystemInfo 函数来获得真正的64位系统信息直接调用GetSystemInfo返回的值和它在64位应用程序中运行所返回的值可能会有所不同。

    void WINAPI GetNativeSystemInfo(
      __out         LPSYSTEM_INFO lpSystemInfo
    );

     

    在应用程序中使用虚拟内存

    预定地址空间区域:

    LPVOID WINAPI VirtualAlloc(

      __in          LPVOID lpAddress,

      __in          SIZE_T dwSize,

      __in          DWORD flAllocationType,

      __in          DWORD flProtect

    );

    lpAddress参数指向内存地址,用来告诉系统我们要预定地址空间中的哪一块。在大多时候只要传NULL就可以了,系统会自动找一块闲置区域。

    dwSize 参数用来指定我们想要预定的大小,以字节为单位。

    flAllocationType 参数用来告诉系统我们到底要预定区域还是调拨物理存储器。假如我们是要预定空间,并想让系统从尽可能高的内存地址来预定区域,那么我们必须给lpAddresNULL,并且传MEM_RESERT | MEM_TOP_DOWN 标志给flAllocationType 参数

    flProtect参数是给区域指定保护属性。最常用的是PAGE_READWRITE

     

    给区域调拨物理存储器

    在预定了区域后,我们还需要给区域调拨物理存储器,这样才能访问其中的内存地址。系统会从页交换文件中来调拨物理存储器给区域。在调拨物理存储器时,起始地址始终都是页面大小的整数倍,整个页面大小也是页面大小的整数倍。

     

    为了调拨物理存储器,我们必须再次调用VirtualAlloc但这次我们会传MEM_COMMIT来做为第三个参数flAllocationType的值。再给物理存储器指定页保护属性的时候,通常我们使用保护属性会和预定区域时相同。

     

    在已经预定的区域中,我们必须告VirtualAlloc函数要调拨多少物理存储器给哪里。这是通过参数lpAddressdwSize 来决定的。值得注意的是,我们无须一下子给整个区域都调拨物理存储器。

     

    下面给出一个例子:

    假如我们的应用程序在地址 5242880 处预定了一块大小为512KB的区域。现在我们希望给该区域从2KB地址开始的地方调拨 6KB 的物理存储器。

    代码如下:

    VirtualAlloc(PVOID (5242880 + 2 * 1024), 6 * 1024 MEM_COMMIT, PAGE_READWRITE);

    在这种情况下,我们必须调拨8KB的物理存储器。

     

    同时预定和调拨物理存储器

    有时,我们想要同时预定区域并给该区域调拨物理存储器。只要调用VirtualAlloc一次就能达到这一目的。

    PVOID  pvMem = VirtualAlloc(NULL, 99*1024, MEM_RESERVE | MEM_COMMIT, PAGE_READWITE);

    撤销调拨物理存储器及释放区域

    BOOL WINAPI VirtualFree(

      __in          LPVOID lpAddress,

      __in          SIZE_T dwSize,

      __in          DWORD dwFreeType

    );

    lpAddress参数应为VirtualAlloc 返回的地址。

    dwSize 参数必须传0

    dwFreeType 参数必须传MEM_RELEASE.

  • 相关阅读:
    Springboot vue 前后分离 跨域 Activiti6 工作流 集成代码生成器 shiro权限
    mybatis3批量更新 批量插入
    Java GC的工作原理详解
    Hadoop的Map侧join
    cut 命令
    head 与 tail
    常用正则
    vim 设置
    Java泛型初探
    linux修改PS1,自定义命令提示符样式
  • 原文地址:https://www.cnblogs.com/forlina/p/2133453.html
Copyright © 2020-2023  润新知