如何将 win32 程序虚拟内存扩展到 3GB?

本文正在参加「金石计划 . 瓜分6万现金大奖」

Windows 内存管理知识总结一篇中,我提到了

  1. 在 Windows 中 32 位进程只能使用 2GB 的虚拟内存
  2. Windows 为我们提供了扩展虚拟内存到 3GB 的方式

本篇会继续介绍

  1. 扩展虚拟内存具体如何操作
  2. 对于线上产品我们应该如何落地此优化

操作过程介绍

BCDEdit \set increaseuserva 3072

  • 在 XP 以上系统,需要用管理员模式打开 cmd,输入 BCDEdit \set increaseuserva 3072,会设置系统参数 increaseuserva 3072,其中 3072 表示 3GB
  • 在 XP 系统,需要修改 boot.ini
  • 详细内容可参考 learn.microsoft.com/zh-cn/windo…

检验设置是否生效

  1. 再次打开 cmd,输入 bcdedit

bcdedit_check.png

  1. 打开我们的应用,使用 vmmap 查看内存情况

vmmap_bcdset.png

图中可以发现,Total + Free 并没有达到 3GB,仍然为 2GB,这是怎么回事呢? 我们除了设置系统参数外,还需要修改应用的链接器参数,下面看看具体要怎么做

设置连接器参数 /LARGEADDRESSAWARE

set_linker.png

  • 我的 Visual Studio 版本比较古老,新版本应该在相似的位置

dump_bin.png

  • dumpbin.exe 在 Microsoft Visual Studio xx\VC\bin\ 目录下
  • 打开 cmd,输入 dumpbin /HEADERS {your exe path},可以看到 Application can handle large (>2GB) address,证明连接器参数设置成功

再次检查

largeaddress_enable.png

  • 可以看到,Total + Free 达到了 3GB

各位仔细算一下~ Total + Free 其实等于 4GB ,这又是怎么回事?

是因为我的 windows 操作系统为 64 位,在 64 位系统上运行 LargeAddress 模式的 32 位程序,虚拟内存会被扩展到 4GB,为什么会有这样的结果,后文会具体介绍。

64 位 Windows 操作系统运行 win32 程序

在 64 位系统上除了上面提到的虚拟内存会被扩展到 4GB,还有额外一点要注意: 不需要像 32 位系统一样设置 bcdedit,只需设置连接器参数 /LARGEADDRESSAWARE 即可。

32位进程虚拟内存地址空间不是一共只有 4GB,为什么在 64 位操作系统下能扩展到 4GB?这不是占用了内核空间么?

实际上,并没有占用内核空间地址。 原因是 32 位进程跑在 64 位系统时,实际是运行的 64 位进程,所以虚拟内存地址空间为 2^64,低地址 4GB 远达不到内核空间的位置。下面附上一张 64 位进程的虚拟地址空间图

64bit_vm_layout.png

如何落地 LargeAddress 优化?

前面提到过,32 位系统下,需要通过 cmd 设置系统参数,我们总不能要求用户去设置吧?那么有没有能让用户花最小代价设置的方案呢?

我们能想到最直接的方案是,在代码中运行 bcdedit 命令,代码如下

ShellExecute(0, L"runas", L"BCDEdit.exe", L"/set increaseuserva 3072", NULL, SW_SHOWNORMAL);
复制代码

遗憾的是,这样做并不能成功,会报找不到 BCDEdit.exe的错误,但直接在 cmd 中却能设置成功,这又是为什么呢?

因为我测试代码还是运行在 64 位系统上,在 64 位跑 32 位进程时,由于文件系统重定向机制(File System Redirection)程序无法访问 SysWOW64 文件夹,所以需要我们做些额外的操作

BOOL bWow64 = false;
IsWow64Process(GetCurrentProcess(), &bWow64);
if (bWow64)
{
  PVOID OldValue = NULL;
  if (Wow64DisableWow64FsRedirection(&OldValue))
  {
    ShellExecute(0, L"runas", L"BCDEdit.exe", L"/set increaseuserva 3072", NULL, SW_SHOWNORMAL);
    Wow64RevertWow64FsRedirection(&OldValue);
  }
}

复制代码

使用上述代码后,可以成功在 64 位系统上设置 bcdedit 系统参数了。更详细可以参考微软社区大神回答我的内容 learn.microsoft.com/en-us/answe…

但是,等等!我们刚刚不是说过 64 位系统不需要设置 bcdedit 么? 好吧,我这步确实走了弯路,不过在解决问题的过程中也学到了更多知识,另外微软社区的大神也希望提问者多了解 Windows 系统的一些机制

reply.png

还要提到的是,通过代码执行 cmd 时,仍会弹出让用户选择是否允许以管理员模式运行 cmd 的界面,只有用户同意打开,系统参数才能设置成功。

LARGEADRRESSAWARE 的风险及如何避免

前面我们详细介绍了如何设置 LargeAddress 模式,那么这么做会有什么风险么?

结论是 不兼容 LargeAddress 模式的代码分配超过 2GB 的高地址时,可能造成指针截断,或者新分配的地址为 0

具体例子我没有调研,猜测是某些程序会分配固定的地址,在地址超过 2GB 时可能取到的地址就变为 0 了,还有可能是某特定类型的指针,只能寻址到 2GB,超过 2GB 的部分会被截断。

那么怎么才能化解风险呢?

需要我们使用 Top Down 内存分配模式去进行全面测试,正常情况下内存地址分配是从低到高的,如果改为从高到低分配,我们就能优先使用 2GB 之上的地址,就可以验证程序中是否存在指针接截断的问题了。

那么,要怎么做呢?

需要修改注册表 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management]

新增 AllocationPreference=00100000,具体看下图提示

reg_table.png

设置成功后,一定要重启计算机后才能生效!

详细可参考 stackoverflow.com/questions/2…

猜你喜欢

转载自juejin.im/post/7166991733366980639