转:如何在32位程序中突破地址空间4G的限制
//如何在32位程序中突破地址空间4G的限制
//首先要获得内存中锁定页的权限 #define _WIN32_WINNT 0x0501 //xp系统
#include <windows.h>
#include <iostream> using std::cout;
using std::endl; BOOL AWESetLockPagesPrivilege( HANDLE hProcess, BOOL Enable )
{
HANDLE Token = NULL;
BOOL Result = FALSE;
TOKEN_PRIVILEGES Info = { }; // 打开令牌
Result = OpenProcessToken ( hProcess, TOKEN_ADJUST_PRIVILEGES, &Token );
if( !Result )
return FALSE; // 设置权限信息
Info.PrivilegeCount = ;
Info.Privileges[].Attributes = Enable? SE_PRIVILEGE_ENABLED : ; // 获得锁定内存权限的ID
Result = LookupPrivilegeValue ( NULL,SE_LOCK_MEMORY_NAME,&(Info.Privileges[].Luid));
if( !Result )
{
CloseHandle( Token );
return FALSE;
} // 调整权限
Result = AdjustTokenPrivileges ( Token, FALSE,(PTOKEN_PRIVILEGES) &Info,, NULL, NULL);
if( ( !Result ) || ( GetLastError() != ERROR_SUCCESS ) )
{
CloseHandle( Token );
return FALSE;
} // 成功返回
CloseHandle( Token );
return TRUE;
} int main()
{ if( !AWESetLockPagesPrivilege( GetCurrentProcess(), TRUE) )
{
// 输出错误信息
cout<<"do not have privilege!"<<endl;
}
const ULONG_PTR ulRAMByte = *; PVOID pvWindow = VirtualAlloc(NULL, ulRAMByte, MEM_RESERVE|MEM_PHYSICAL, PAGE_READWRITE); SYSTEM_INFO sinf; GetSystemInfo(&sinf); ULONG_PTR ulRAMPages = ulRAMByte/sinf.dwPageSize; cout<<"original: "<<ulRAMPages<<endl;
ULONG_PTR aRAMPages[/]; if(!AllocateUserPhysicalPages(GetCurrentProcess(), &ulRAMPages,aRAMPages))
cout<<"fail to allocate!"<<endl;
cout<<GetLastError()<<endl; cout<<"after allocate: "<<ulRAMPages<<endl;
MapUserPhysicalPages(pvWindow, ulRAMPages, aRAMPages);
Sleep();
FreeUserPhysicalPages(GetCurrentProcess(), &ulRAMPages, aRAMPages); VirtualFree(pvWindow, , MEM_RELEASE);
return ;
}
为运行以上代码,博主建立了实验环境,如下:
1.操作系统:Microsoft windows server 2003 企业版SP2
2.vs:vs 2003
3.系统分配内存为:8GB
由于该环境为在hyper-v上建立的虚拟机,内存为热添加,所以/PAE开关自动启用,没有手工启用/PAE开关.
运行结果如下:
众所周知,所有的32位应用程序都有4GB的进程地址空间,因为32位地址最多可以映射4GB的内存(对于虚拟地址空间概念不 太熟悉的朋友建议去看一下《Windows核心编程》这本书)。对于Microsoft Windows操作系统,应用程序可以访问2GB的进程地址空间(32位Linux可以访问3GB地址空间),这就是称为用户模式的虚拟地址空间。这 2GB的用户模式虚拟地址空间位于4GB地址空间的低一半,而与之相对应的高一半2GB地址空间由操作系统内核使用,因此被成为内核模式的虚拟地址空间。 在一个进程中,所有的线程读共享相同的2GB用户模式虚拟地址空间。 对于一般的应用程序来说,2GB的地址空间是足够使用的了,但是对于一些特殊的需要使用海量内存的应用程序(典型的例子是数据库系统)来说,2GB的地址 空间就远远不够了。为了缓解地址空间的不足,微软提供了一个权宜的解决方案,所有从Windows 2000 Server开始的操作系统版本都提供了一个boot.ini启动开关(/3GB),可以为应用程序提供访问3GB的进程地址空间的能力,从而将内核模式 的地址空间限定为1GB。以下就是一个开启了3GB选项的boot.ini文件示例:
虽然使用/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 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 );
各个函数的具体参数含义可以参考MSDN,其中AllocateUserPhysicalPagesNuma是Windows Vista和Windows 2008 Server新增的函数,用于支持NUMA(非一致性内存访问)。以下就简单说一下如何使用这几个API函数来达到使用超过4GB的内存。 使用AllocateUserPhysicalPages函数分配需要的物理内存,使用方式如下:
// 检查分配内存是否成功
需要注意的是,调用上述代码的用户必须具有“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 = { }; // 打开令牌
Result = OpenProcessToken ( hProcess, TOKEN_ADJUST_PRIVILEGES, &Token );
if( !Result )
return FALSE; // 设置权限信息
Info.PrivilegeCount = ;
Info.Privileges[].Attributes = Enable? SE_PRIVILEGE_ENABLED : ; // 获得锁定内存权限的ID
Result = LookupPrivilegeValue ( NULL,SE_LOCK_MEMORY_NAME,&(Info.Privileges[].Luid));
if( !Result )
{
CloseHandle( Token );
return FALSE;
} // 调整权限
Result = AdjustTokenPrivileges ( Token, FALSE,(PTOKEN_PRIVILEGES) &Info,, 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,,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的限制的更多相关文章
- 关于32位程序在Win7&64位系统中连接Microsoft Excel数据源的问题
最近在新公司电脑上跑以前的selenium测试框架的时候,抛出了如下的错误 出现的是ODBC Driver问题:[Microsoft][ODBC Driver Manager] Data source ...
- C# 32位程序在64位系统下运行中解决重定向问题
在64位的Windows操作系统中,为了兼容32位程序的运行,64位的Windows操作系统采用重定向机制.目的是为了能让32位程序在64位的操作系统不仅能操作关键文件文夹和关键的注册表并且又要避免与 ...
- 记32位程序(使用3gb用户虚拟内存)使用D3DX9导致的一个崩溃的问题
为了增加32位程序的用户虚拟内存的使用量,我们使用了/LARGEADDRESSAWARE编译选项来使32位程序可能使用到3gb的内存,能否使用到3gb内存也跟平台.系统和设置有关系,现摘抄部分作为参考 ...
- Ubuntu14.04 64位运行32位程序
最近公司新增的机器安装Ubuntu14.04 64bit导致之前在32bit下编译的Qt工具软件无法运行. 于是google的了一下找到一些解决办法,但不能保证全部32bit的Qt程序都能正常,测试了 ...
- C# 32位程序访问64位系统注册表
原文:C# 32位程序访问64位系统注册表 我的上一篇文章已经阐述了“32位程序和64位程序在64位平台上读\写注册表的区别”,那么接下来将要回答上篇所留下来的一个问题:32位程序如何访问64位系统注 ...
- Linux64位程序中的漏洞利用
之前在栈溢出漏洞的利用和缓解中介绍了栈溢出漏洞和一些常见的漏洞缓解 技术的原理和绕过方法, 不过当时主要针对32位程序(ELF32). 秉承着能用就不改的态度, IPv4还依然是互联网的主导, 更何况 ...
- win764位系统上让32位程序能申请到4GB内存方法
win764位系统上让32位程序能申请到4GB内存方法. 2016年09月18日 18:36:26 阅读数:1550 最近测试一个32位程序总是在1.2G左右内存时崩溃,怀疑是内存申请失败,本身32位 ...
- 通过修改EIP寄存器实现32位程序的DLL注入
功能:通过修改EIP寄存器实现32位程序的DLL注入 <如果是64位 记得自己对应修改汇编代码部分> 原理:挂起目标进程,停止目标进程EIP的变换,在目标进程开启空间,然后把相关的指令机器 ...
- 使 32 位程序使用大于 2GB 的内存
不管在 32 位 Windows 上还是在 64 位 Windows 上,32 位的应用程序都只能使用最大 2GB 的内存,这是我们司空见惯的一个设定.但其实 Windows 提供了一些方法让我们打破 ...
随机推荐
- github邮箱验证技巧
申请的github账号,绑定邮箱之后才能创建库,而反复几次的发送邮件均为收到验证邮件,猜测有两个原因: 1.腾讯邮件服务器屏蔽了github的来信 (腾讯不会这么狭隘的,×) 2.自己邮箱的域名黑名单 ...
- MySQL主主双机负载均衡
MySQL双机主主架构,其上辅以负载均衡设备,可以实现mysql数据库的负载均衡高性能和高可用性,负载均衡设备可以根据算法将数据库操作的负 载平均分到两台MySQL服务器上,这样对于每台服务器来说工作 ...
- gitlab+gerrit+jenkins持续集成框架
1.持续集成之gitlab+gerrit+jenkins 1.1. GitLab 1.1.1. 简介 GitLab 是一个使用使用Ruby on Rails搭建的,用于仓库管理系统的开源项目.使用Gi ...
- (视频) 《快速创建网站》2.1 在Azure上创建网站及网站运行机制
现在让我们开始一天的建站之旅. 本文是<快速创建网站>系列的第2篇,如果你还没有看过之前的内容,建议你点击以下目录中的章节先阅读其他内容再回到本文. 访问本系列目录,请点击:http:// ...
- 安卓+servlet+MySql 查询+插入(汉字乱码解决)
问题: 安卓程序,通过servlet连接MySQL数据库,并实现查询和插入(修改,删除类似). 其中遇到的最大的问题是:汉字乱码问题(查询条件有汉字乱码.servlet的汉字到数据乱码.安卓通过ser ...
- javascript-单例模式
单例模式笔记 也称为单体模式,只允许实例化一次的对象类 用法: 1.命名空间:用一个对象来规划一个命名空间,井井有条的管理对象上的属性和方法 2.静态变量管理:让创建的函数 ...
- python极客学院爬虫V1
定向爬取极客学院视频,原本只有年费VIP只能下载,经过分析,只要找个免费体验VIP即可爬取所有视频 涉及的基本技术:python xpath 正则 com+ 通过python调用迅雷从组件,实现自动创 ...
- python入门综合
#!/usr/bin/env python#-*-coding:utf-8-*- #以上是配置编写环境的开始 #第一行env表示运行当前环境变量内的python版本(2.x or 3.x)#第二行 ...
- ResultSet rs = stmt.executeQuery(sql); 返回值问题判断
JAVA ResultSet rs = stmt.executeQuery(sql); //查询返回的结果集不管是否查到,rs都不是null,那么问题是怎么判断查找不到来执行一个提示“账号或者 ...
- URL编码知识摘抄备忘
网页工具 http://www.107000.com/T-UrlEncode/ 参考: 维基百科http://zh.wikipedia.org/zh/%E7%99%BE%E5%88%86%E5%8F% ...