众所周知,所有的32位应用程序都有4GB的进程地址空间,因为32位地址最多可以映射4GB的内存。对于Microsoft Windows操作系统,应用程序可以访问2GB的进程地址空间(32位Linux可以访问3GB地址空间),这就是称为用户模式的虚拟地址空间。这2GB的用户模式虚拟地址空间位于4GB地址空间的低一半,而与之相对应的高一半2GB地址空间由操作系统内核使用,因此被成为内核模式的虚拟地址空间。在一个进程中,所有的线程读共享相同的2GB用户模式虚拟地址空间。
    对于一般的应用程序来说,2GB的地址空间是足够使用的了,但是对于一些特殊的需要使用海量内存的应用程序(典型的例子是数据库系统)来说,2GB的地址空间就远远不够了。为了缓解地址空间的不足,微软提供了一个权宜的解决方案,所有从Windows 2000 Server开始的操作系统版本都提供了一个boot.ini启动开关(/3GB),可以为应用程序提供访问3GB的进程地址空间的能力,从而将内核模式的地址空间限定为1GB。以下就是一个开启了3GB选项的boot.ini文件示例:

[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)WINDOWS="Windows Server 2003, Enterprise" /fastdetect  /3GB
   虽然使用/3GB选项能够将用户模式的地址空间扩大50%(从2GB增加到3GB),但是对于数据库系统这样的应用程序来说,这1GB的地址空间的增加只能是杯水车薪,并不能解决多少问题,而且由于操作系统内核只能使用1GB地址空间,这样可能会给操作系统的运行带来一定的负面影响,因此除非没有更好的解决方案,是不建议使用/3GB方式的。

鉴于像数据库系统这样的应用程序对海量内存的需求,Intel公司也觉得4GB的内存不够用,因此就将CPU芯片中内存地址线由32根扩展到了36根(即最多64GB),这就是所谓的物理地址扩展(PAE:Physical Address Extension)。PAE使得操作系统或应用程序能够最多使用64GB的物理内存,对于Windows系统(2000以上)来说,只需在boot.ini文件中使用/PAE选项即可(类似于上面的/3GB选项)。需要提醒大家的是,如果没有在boot.ini文件中使用/PAE选项,那么即使计算机已经配置了超过4GB的物理内存,在Windows操作系统中也不能使用超过4GB的那些内存(事实上,根据我的经验,如果没有使用/PAE选项,Windows系统最多只能识别3.25GB的物理内存,我也不清楚为什么不是4GB?如果有知道的,请告诉我一声)。
    虽然PAE使得在应用程序中使用超过4GB的物理内存成为可能,但是由于32位应用程序的虚拟地址空间并不随着物理内存的增大而有任何变化,这意味着你不可能使用类似VirtualAlloc( GetCurrentProcess,2GB,...,...)这样的函数=调直接分配接近用户模式地址空间大小的内存区域。为了突破32位地址空间的限制,需要使用一种被成为地址窗口扩展(AWE:Address Windowing Extensions)的机制(参见上图)。
    AWE是Windows的内存管理功能的一组扩展,它使应用程序能够使用的内存量超过通过标准32位寻址可使用的2~3GB内存。AWE允许应用程序获取物理内存,然后将非分页内存的视图动态映射到32位地址空间。虽然32位地址空间限制为4GB,但是非分页内存却可以远远大于4GB。这使需要大量内存的应用程序(如大型数据库系统)能使用的内存量远远大于32位地址空间所支持的内存量。
    在使用AWE机制时,需要注意以下几点:
    (1)AWE允许在32位体系结构上分配超过4GB的物理内存,只有当系统可用物理内存大于用户模式的虚拟地址空间时,才应该使用AWE。
    (2)若要使32位操作系统支持4GB以上的物理内存,必须在Boot.ini文件启用/PAE选项。
    (3)若在Boot.ini文件中启用了/3GB选项,则操作系统最多能够使用16GB的物理内存,因此如果实际的物理内存超过16GB,必须确保不使用/3GB选项。
    (4)使用AWE分配的内存是非分页的物理内存,这意味着这部分内存只能由分配的应用程序独占使用,不能由操作系统或其他程序使用,直到这些内存被释放为止,这与通常的VirtualAlloc函数分配的虚拟内存存在显著的不同,它不会参与分页替换。

在Windows中,跟AWE相关的API函数有以下几个:

BOOL AllocateUserPhysicalPages(
  HANDLE hProcess,
  PULONG_PTR NumberOfPages,
  PULONG_PTR UserPfnArray
);

BOOL WINAPI AllocateUserPhysicalPagesNuma(
  HANDLE hProcess,
  PULONG_PTR NumberOfPages,
  PULONG_PTR PageArray,
  DWORD nndPreferred
);

BOOL MapUserPhysicalPages(
  PVOID lpAddress,
  ULONG_PTR NumberOfPages,
  PULONG_PTR UserPfnArray
);

BOOL MapUserPhysicalPagesScatter(
  PVOID* VirtualAddresses,
  ULONG_PTR NumberOfPages,
  PULONG_PTR PageArray
);

BOOL FreeUserPhysicalPages(
  HANDLE hProcess,
  PULONG_PTR NumberOfPages,
  PULONG_PTR UserPfnArray
);

各个函数的具体参数含义可以参考MSDN,其中AllocateUserPhysicalPagesNuma是Windows Vista和Windows 2008 Server新增的函数,用于支持NUMA(非一致性内存访问)。以下就简单说一下如何使用这几个API函数来达到使用超过4GB的内存。
    使用AllocateUserPhysicalPages函数分配需要的物理内存,使用方式如下:

ULONG_PTR NumberOfPages = xxx; // 需要分配的内存页数
ULONG_PTR *aPFNs               = new ULONG_PTR[NumberOfPages];
BOOL bResult                            = AllocateUserPhysicalPages( GetCurrentProcess(),&NumberOfPages,aPFNs);

// 检查分配内存是否成功
if(!bResult)
{
   // 分配识别,错误处理
   // .....
}

// 检查实际分配的内存页数
if( NumberOfPages != xxx )
{
     // ....
}
    需要注意的是,调用上述代码的用户必须具有“Lock Pages in Memory”(内存中锁定页面)的权限。此权限使得用户可以使用进程将数据保持在物理内存中,这样可防止系统将数据分页到磁盘上的虚拟内存中。行使此权限会因降低可用随机存取内存(RAM)的数量而显著影响系统性能。需要在本地安全策略管理程序中给用户赋予该权限,如下图所示:

给用户分配了上述权限之后,需要在程序中使用代码启用该权限,如下所示:

// 设置锁住物理内存的权限,此代码在调用AllocateUserPhysicalPages之前执行
if( !AWESetLockPagesPrivilege( GetCurrentProcess(), TRUE) )
{
    // 输出错误信息
    ..........
}

/// <summary>
///        设置或清除启用AWE( Address Windowing Extensions )所需要的锁住内存的权限。
/// </summary>
/// <param name="hProcess">
///        进程句柄。
/// </param>
/// <param name="Enable">
///        设置或者清除标志。
/// </param>
/// <returns>
///        如果成功,则返回TRUE,否则返回失败。
/// </returns>
BOOL AWESetLockPagesPrivilege( HANDLE hProcess, BOOL Enable )
{
    HANDLE                Token    = NULL;
    BOOL                Result    = FALSE;
    TOKEN_PRIVILEGES    Info    = { 0 };

// 打开令牌
    Result = OpenProcessToken ( hProcess, TOKEN_ADJUST_PRIVILEGES, &Token );
    if( !Result )
        return FALSE;

// 设置权限信息
    Info.PrivilegeCount = 1;
    Info.Privileges[0].Attributes = Enable? SE_PRIVILEGE_ENABLED : 0;

// 获得锁定内存权限的ID
    Result = LookupPrivilegeValue ( NULL,SE_LOCK_MEMORY_NAME,&(Info.Privileges[0].Luid));
    if( !Result )
    {
        CloseHandle( Token );
        return FALSE;
    }

// 调整权限
    Result = AdjustTokenPrivileges ( Token, FALSE,(PTOKEN_PRIVILEGES) &Info,0, NULL, NULL);
    if( ( !Result )  || (  GetLastError() != ERROR_SUCCESS ) )
    {
        CloseHandle( Token );
        return FALSE;
    }

// 成功返回
    CloseHandle( Token );
    return TRUE;
}

使用AllocateUserPhysicalPages分配了物理内存之后,下一步就是使用MapUserPhysicalPages或MapUserPhysicalPagesScatter函数将物理内存映射进用户模式地址空间内,这两个函数用法差不多,只是第一个参数有差别。由于分配的物理内存的大小超过了用户模式地址空间的大小,因此显然不可能一次将所有的物理内存都映射到地址空间中。通常的做法是在用户模式地址空间内分配一小块连续的区域(即地址窗口),然后根据使用的需要动态将部分的物理内存映射到地址空间,这也就是“地址窗口扩展”一词的真实含义。代码示例如下:

// 定义16M的地址窗口
#define MEMORY_REQUESTED (16*1024*1024)

// 分配地址窗口
PVOID lpMemReserved = VirtualAlloc( NULL,MEMORY_REQUESTED, MEM_RESERVE | MEM_PHYSICAL,PAGE_READWRITE );

// 将物理内存映射到地址空间(根据需要,每次映射的页面会不同,
// 即下面函数的第三个参数aPFNs会指向不同的物理页)
bResult = MapUserPhysicalPages( lpMemReserved,NumberOfPages,aPFNs);

// 以下就像普通的内存一样使用lpMemReserved 指针来操作物理内存了
...................
    使用完了之后,可以使用FreeUserPhysicalPages来释放分配的物理内存,示例如下:

// 取消内存映射
bResult = MapUserPhysicalPages( lpMemReserved,NumberOfPages,NULL );

// 释放物理内存
bResult = FreeUserPhysicalPages( GetCurrentProcess(),&NumberOfPages,aPFNs );

// 释放地址窗口
bResult = VirtualFree( lpMemReserved,0,MEM_RELEASE );

// 释放物理页号数组
delete[] aPFNs;
    AWE机制被使用最多的一个场合是数据库系统的缓存管理器(BufferManager),例如SQL Server的内存管理器。虽然以上代码都是基于Windows操作系统,但是PAE和AWE机制并不是Windows特有的,32位Linux也有类似的API。完整使用AWE机制的例子,大家可以参考MySQL的源码。
    最后想说的是,对于开发人员来说,一个好消息是64位CPU和操作系统正越来越普及。在64位环境下,一个进程的用户模式的地址空间可达8TB(也就是说目前很多的64位系统只使用了40几位的内存地址,远没有充分使用64位的内存地址),在可以预见的未来很长一段时间,估计我们都不会再为地址空间不足而发愁了,让我们一起为64位时代的到来而欢呼吧!

32位程序使用超过4G的内存相关推荐

  1. 32位程序使用超过4G的内存 实例

    最近在做个程序,虽然是小型程序,但是使用的内存量却很大,动辄达到10G.在64位系统上可以轻松实现,无奈我是基于32位的系统进行开发,程序还没跑起来就已经被终止了.       试过很多办法,包括文件 ...

  2. 如何使32位Win7支持超过4GB的内存,而不装64位

    如何使32位Win7支持超过4GB的内存 让32位系统支持更大的内存超过4G [情况参数:] PC: 联想商用台式机,M4350 RAM: 1600, DDR3 , 2GB OS: Win7 专业版 ...

  3. 32位程序在Windows上的内存问题

    问题描述: 32位的程序运行在Win2016上,内存增加到1.4G左右就崩溃了. 原因分析: 默认情况下,32位程序虚拟地址空间是4G,其中2G为内核空间,系统使用:2G为用户虚拟地址空间,是用户可申 ...

  4. 64位系统上运行32位程序的内存限制

    64位系统上运行32位程序能否申请到8G内存? 收藏到手机    转发   评论 2010-01-20 10:56 申请不到,因为64为系统在运行32位程序的时候只是为了向下兼容而已,对于32位程序来 ...

  5. 32位CPU最大支持4G内存

    32位系统最大只能支持4GB内存之由来 也许大家对这个问题都不陌生,实际装过系统用过电脑的朋友可能都有这样的经历:自己电脑配的是4G的内存条,可是装完系统之后发现电脑上显示的只有3.2G左右可用内存, ...

  6. c# 32位程序突破2G内存限制

    起因 在开发过程中,由于某些COM组件只能在32位程序下运行,程序不得不在X86平台下生成.而X86的32位程序默认内存大小被限制在2G.由于程序中可能存在大数量处理,期间对象若没有及时释放或则回收, ...

  7. 32位系统能够识别多达内存_C ++程序可打印多达N个术语的卢卡斯系列

    32位系统能够识别多达内存 Given N and we have to print lucas series upto N terms. 给定N,我们必须打印lucas系列,最多N个术语. 卢卡斯系 ...

  8. 32位程序和64位程序这些区别你知道吗?

    我们在编写C/C++程序时,32位程序和64位程序的代码有何区别?如何编写既可以编译成32位程序又可以编译成64位程序的代码? 代码上的区别 实际上,对于32位程序和64位程序来说,代码上的区别不大, ...

  9. 32位程序运行内存1G以上容易卡死或崩溃问题

    32位程序默认情况下最大运行内存为2G,一般情况下1G以上就容易出现异常,1.5G时可能会导致程序崩溃,如果是vs工程,此情况下可尝试修改最大内存地址为4G,修改方式如下: 项目-属性-链接器-系统- ...

  10. 32位程序和64位程序

    我们有时候会遇到这样一种情况:我们在32位操作系统上下载了64位版本的程序,却发现无法安装,大家可能会很困惑. 下面就来简单解释一下32位程序与64位程序的具体区别. 我们首先要弄明白的: 1.这里所 ...

最新文章

  1. 基因课 15天入门生物信息(2021年) 第三天 Linux基础命令(1)
  2. 5.hadoop常用命令
  3. Python基础(6)_函数
  4. K/3Cloud 分页报表示例参考
  5. softsign与tanh的比较
  6. 快手2020校园招聘秋招笔试--工程B试卷
  7. Nginx学习笔记---ngx_table_elt_t数据结构
  8. webapi文档描述-swagger
  9. Java中timer的schedule()和schedualAtFixedRate()函数的区别
  10. 网上论坛生成测试数据方法(抢分利器)
  11. mysql 好用 客户端_5款好用的mysql客户端
  12. mysql关联查询语句
  13. intellij idea 汉化包
  14. mac打开网页速度特别慢
  15. vue项目中如何下载excel表格模板
  16. python str怎么用_python的str函数怎么使用
  17. 【Spring】context:component-scan包扫描问题
  18. linux 软件包kbd 位置,6.42. Kbd-1.12
  19. Numpy:开源的科学计算库
  20. Unity:DOTween来回运行或者缩放等LoopType.Yoyo

热门文章

  1. XCTF练习题---MISC---simple_transfer
  2. HDOJ 2856 How far away ?
  3. 计算机装系统找不到硬盘分区,系统安装找不到现有分区,小编教你如何解决系统安装找不到现有分区...
  4. 电动汽车(EV)无线充电(WPT)及标准
  5. 在家如何下载各大权威数据库文献
  6. PHP 对和队列的区别,【php】Redis的队列和消息队列的区别是什么?
  7. 去掉WORD文档中首页的页眉及页眉的横线
  8. word页眉页脚修改
  9. OpenGL ES 理解纹理与纹理过滤
  10. 可行性分析与需求分析