在 64 位(或 32 位)Windows 上以 32 位进程访问 >2、3、4GB 文件

Posted

技术标签:

【中文标题】在 64 位(或 32 位)Windows 上以 32 位进程访问 >2、3、4GB 文件【英文标题】:Accessing >2,3,4GB Files in 32-bit Process on 64-bit (or 32-bit) Windows 【发布时间】:2013-03-25 17:00:47 【问题描述】:

免责声明:我为这个问题的冗长道歉(不过我认为这是一个有趣的问题!),但我不知道如何更简洁地表达它。

对于在 64 位 Windows 7 上解决在 32 位进程中访问多 GB 文件的问题,我已经进行了数小时的研究,范围从 /LARGEADDRESSAWAREVirtualAllocEx AWE。我对在 Windows 中编写多视图内存映射系统(CreateFileMapping、MapViewOfFile 等)感到有些自在,但还是无法完全摆脱对这个问题有更优雅的解决方案的感觉。此外,我非常了解 Boost 的进程间和 iostream 模板,尽管它们看起来相当轻量级,需要类似的努力来编写仅使用 Windows API 调用的系统(更不用说我已经有一个内存的事实 -映射架构使用 Windows API 调用半实现)。

我正在尝试处理大型数据集。该程序依赖于预编译的 32 位库,这就是为什么目前该程序本身也在 32 位进程中运行,即使系统是 64 位,具有 64 位操作系统。我知道有一些方法可以围绕它添加包装库,但是,鉴于它是更大代码库的一部分,这确实是一项艰巨的任务。我将二进制标头设置为允许/LARGEADDRESSAWARE(以减少我的内核空间为代价?),这样我每个进程可以获得大约 2-3 GB 的可寻址内存,给予或接受(取决于堆碎片等) .).

问题出在:数据集为 4+GB,并且在其上运行 DSP 算法,这需要对文件进行本质上的随机访问。指向从文件生成的对象的指针在 C# 中处理,但文件本身在 C++ 中加载到内存中(使用此部分内存映射系统)(它是 P/Invoked)。因此,不幸的是,我认为解决方案并不像简单地调整窗口以访问我需要访问的文件部分那么简单,因为本质上我仍然希望将整个文件抽象为一个指针,我可以从中调用方法访问文件中几乎任何位置的数据。

显然,大多数内存映射架构都依赖于将单个进程拆分为多个进程。因此,例如,我将使用 3 个进程访问一个 6 GB 的文件,每个进程都有一个 2 GB 的文件窗口。然后我需要添加大量逻辑来从这些不同的窗口/进程中提取和重组数据。 VirtualAllocEx 显然提供了一种增加虚拟地址空间的方法,但我仍然不完全确定这是否是最好的方法。

但是,假设我希望这个程序像 64 位系统上的单个 64 位进程一样“轻松”运行。假设我不关心抖动,我只想能够操作系统上的一个大文件,即使任何时候只有 500 MB 加载到物理 RAM 中。有没有什么方法可以在不必手动编写有点荒谬的手动记忆系统的情况下获得此功能?或者,有没有比我迄今为止通过结合 SO 和互联网找到的更好的方法?

这引出了第二个问题:有没有办法限制这个进程使用多少物理 RAM?例如,如果我想将进程限制为在任何时候仅将 500 MB 加载到物理 RAM 中(同时保持多 GB 文件在磁​​盘上分页)?

对于这个冗长的问题,我感到很抱歉,但我觉得这是对我在 SO 和整个网络上找到的许多问题(只有部分答案)的一个不错的总结。我希望这可以是一个可以充实明确答案(或至少一些优点/缺点)的领域,并且我们都可以在此过程中学到一些有价值的东西!

【问题讨论】:

tl;dr,但是如果您想使用外部库:Qt 可以跨平台处理“大文件”,尽管不是内存映射(在 32 位平台上)。但它使用可能达到可比性能的内部 IO 缓存? 感谢 leemes 的评论(尽管 tl;dr 说起来有点粗鲁>_>;;)!我已经研究过使用内部 IO 缓存,但这似乎引入了相当多的复杂性,这是我试图避免的。另外,我想避免添加额外的库,例如 Qt(不过,Boost 已经集成) 限制 RAM,您可以使用 Windows 作业对象并限制工作集,即限制物理 RAM。不幸的是,我认为这只会最大化交换,所以这不是你想要的。我还假设您了解内存映射文件,但它们不符合您的需求。 【参考方案1】:

你可以编写一个访问器类,给它一个基地址和一个长度。如果出现错误条件(超出范围等),它会返回数据或抛出异常(或者您想通知错误条件)。

然后,任何时候你需要从文件中读取,访问器对象可以在调用ReadFile()之前使用SetFilePointerEx()。然后,您可以将访问器类传递给您在读取文件时创建的任何对象的构造函数。然后对象使用访问器类从文件中读取数据。然后它将数据返回给对象的构造函数,该构造函数将其解析为对象数据。

如果稍后您能够编译为 64 位,则只需更改(或扩展)访问器类以改为从内存中读取。

至于限制进程使用的 RAM 量.. 这主要是确保 A)你没有内存泄漏(尤其是淫秽的)和 B)摧毁你现在不需要的物体。即使您稍后会需要它,但数据不会改变......只需销毁对象。然后在您确实需要时重新创建它,允许它从文件中重新读取数据。

【讨论】:

嗯...这听起来确实是一个有趣的想法,inetknght!谢谢你的反馈!虽然这听起来像是一种读取文件的好方法,但它仍然需要添加大量逻辑来手动跨文件移动视图,尽管我同意使用可扩展访问器会很棒,在如果将其移向 64 位,则只需修改访问器类。如果没有任何其他方法可以轻松地让文件由其他构造管理,这可能最终成为最终实现。

以上是关于在 64 位(或 32 位)Windows 上以 32 位进程访问 >2、3、4GB 文件的主要内容,如果未能解决你的问题,请参考以下文章

在 64 位机器上以 32 位运行 C# 应用程序

如何在 64 位机器上以 32 位模式运行 VBScript?

从用户代理或 Javascript 检测 64 位或 32 位 Windows?

32 位应用程序无法在 x64 Win2003 上运行,原因是

在 64 位 Windows 上安装 32 位或 64 位应用程序有啥区别?

检测32位或64位Windows