新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 计算机科学论坛计算机技术与应用『 C/C++编程思想 』 → 使用AWE分配內存 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 7664 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: 使用AWE分配內存 举报  打印  推荐  IE收藏夹 
       本主题类别: Description Logics    
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客楼主
    发贴心情 使用AWE分配內存

    注:获得权限之后要重启系统或者注销,我也是看了windows 核心编程才查的代码,获得好大的收获,所以分享了。

    先贴上我的源代码:
    //如何在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    = { 0 };

    // 打开令牌
    Result = OpenProcessToken ( hProcess, TOKEN_ADJUST_PRIVILEGES, &Token );
    if( !Result )
    return FALSE;
    A
    // 设置权限信息
    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;
    }

    int main()
    {

    if( !AWESetLockPagesPrivilege( GetCurrentProcess(), TRUE) )
    {
    // 输出错误信息
    cout<<"do not have privilege!"<<endl;
    }
    const ULONG_PTR ulRAMByte = 1024000*1024;

    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[1024000/4];

    if(!AllocateUserPhysicalPages(GetCurrentProcess(), &ulRAMPages,aRAMPages))
    cout<<"fail to allocate!"<<endl;
    cout<<GetLastError()<<endl;

    cout<<"after allocate: "<<ulRAMPages<<endl;
    MapUserPhysicalPages(pvWindow, ulRAMPages, aRAMPages);
    Sleep(10000);
    FreeUserPhysicalPages(GetCurrentProcess(), &ulRAMPages, aRAMPages);

    VirtualFree(pvWindow, 0, MEM_RELEASE);
    return 0;
    }

    众所周知,所有的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文件示例:
    [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机制的例子,大家可以参考[URL=http://www.mysql.org/]MySQL[/URL]的源码。
    最后想说的是,对于开发人员来说,一个好消息是64位CPU和操作系统正越来越普及。在64位环境下,一个进程的用户模式的地址空间可达8TB(也就是说 目前很多的64位系统只使用了40几位的内存地址,远没有充分使用64位的内存地址),在可以预见的未来很长一段时间,估计我们都不会再为地址空间不足而 发愁了,让我们一起为64位时代的到来而欢呼吧!


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2010/10/25 15:48:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/11/25 16:31:28

    本主题贴数1,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    62.500ms