快速RAM中的动态内存分配

8
在Windows 32位和64位机器上,我需要分配内存来存储大量实时流数据,总计约1GB。如果我使用malloc(),我将获得一个虚拟内存地址,这个地址可能会导致一些页面交换到硬盘,具体取决于我拥有的内存量。不幸的是,我担心硬盘会影响性能并导致数据丢失。
是否有一种方法可以强制内存仅在RAM中分配,即使这意味着在没有足够内存可用的情况下会出现错误(因此用户需要关闭其他应用程序或使用另一台机器)?我想保证所有操作都将在内存中完成。如果失败,强制应用程序退出是可以接受的。
我知道另一个进程可能会占用一些内存,但我不担心,因为在这台机器上没有发生这种情况(它将是该机器上唯一执行此大型分配的应用程序)。
[编辑:] 迄今为止,我的尝试是尝试使用VirtualLock,如下所示:
if(!SetProcessWorkingSetSize(this, 300000, 300008))
    printf("Error Changing Working Set Size\n");

// Allocate 1GB space
unsigned long sz = sizeof(unsigned char)*1000000000;
unsigned char * m_buffer = (unsigned char *) malloc(sz);

if(m_buffer == NULL)
{
    printf("Memory Allocation failed\n");
}
else
{
    // Protect memory from being swapped
    if(!VirtualLock(m_buffer , sz))
    {
           printf("Memory swap protection failed\n");
    }           
}

但是工作集的更改失败了,VirtualLock 也是如此。 Malloc 返回非空值。
[编辑2] 我也尝试了:
 unsigned long sz = sizeof(unsigned char)*1000000000;
 LPVOID lpvResult;
 lpvResult = VirtualAlloc(NULL,sz, MEM_PHYSICAL|MEM_RESERVE, PAGE_NOCACHE);

但是lpvResult的值为0,所以那里也没有运气。


@ScottChamberlain:看起来确实很相似。但是他们正在使用.NET。我正在研究VirtualLock函数。 - Gustavo Litovsky
@GustavoLitovsky 我没有注意到这个问题是关于 .net 的,但所有的答案都是 P/Invoke API 调用,所以仍然相关。 - Scott Chamberlain
1
@ScottChamberlain:那个问题作为指针很好,但是在阅读更多内容后,发现还有更多需要注意的地方(尽管它的标签可能需要调整)。似乎我还需要调用SetProcessWorkingSetSize并增加最小和最大值以确保我可以获取所需的页面。 - Gustavo Litovsky
能否在全局范围内创建一个1GB大的数组? - andre
我同意自己的观点。我从未找到其他问题,而我的问题引发了其他问题,我认为这对那些寻找答案的人来说将是重要的。 - Gustavo Litovsky
显示剩余12条评论
5个回答

6
你可以使用mlock,mlockall,munlock,munlockall函数来防止页面被交换(POSIX的一部分,在MinGW中也可用)。不幸的是,我没有Windows的经验,但看起来VirtualLock可以做同样的事情。
希望能帮到你。祝好运!

谢谢。我正在使用Visual Studio进行这个特定的应用程序,但以防我尝试在MingW或Linux中做同样的事情,我会记住这些内容的。 - Gustavo Litovsky
我的理解是VirtualLock并不完全符合您的要求。它确实可以“锁定内存中的页面”,但我记得在某个地方读到过,这仅适用于当您的进程实际运行时,而不是线程暂停时。当线程暂停时,它们很可能被分页出去,并在需要再次运行线程时重新分页回来。(我不是100%确定;这在MSDN文档中没有记录,但我相当确定我在某个地方读到过。显然,如我在答案中所述使用选项2可以解决此问题,但同样也不能保证。) - user541686

4
我认为VirtualAlloc可能会提供你所需的一些内容。
这个问题实际上归结为编写自己的内存管理器,而不是使用CRT函数。

在我的情况下,我很幸运,因为这些是固定大小的数据框,并且我希望将它们全部连续放置,因此应该很容易管理。只需要确保分配了内存并且不会移动即可。 - Gustavo Litovsky
是的,看起来 VirtualLock 可以做到。我正在考虑是否还需要使用 SetProcessWorkingSetSize 来增加工作集。你知道我是否需要这样做吗? - Gustavo Litovsky
2
我在VirtualAlloc中没有看到锁定页面的选项。Vlad提供的VirtualLock答案似乎是正确的。SetProcessWorkingSetSize也可以工作,但它不能保证进程的所有页面都不会被分页出去。 - Alois Kraus
我看到PAGE_NOCACHE是保护中的一个标志。我会试一试,看看它是否运行。如果有办法真正测试它不被缓存,我会很感兴趣。 - Gustavo Litovsky
@GustavoLitovsky:一定有办法。寻找getrusage()的Windows替代方案,以及它的硬页故障和交换计数器。 - user405725
显示剩余2条评论

2
你需要使用未记录的NtLockVirtualMemory函数,使用锁定选项2(LOCK_VM_IN_RAM)。确保您首先请求并获得SE_LOCK_MEMORY_NAME特权,并且要知道它可能不会被授予(我确定组策略默认将特权授予谁,但很可能没有人被授予)。
我建议使用VirtualLock作为备选方案,如果也失败了,则使用SetProcessWorkingSetSize。如果那个也失败了,那就让它失败吧...
请参见this link以获取一些关于此的有趣讨论。其中一个人说:
当您指定 LOCK_VM_IN_WSL 标志时,您只是告诉平衡集管理器,您不希望某些特定页面被交换到磁盘上,并要求在修剪目标进程的工作集时将此页面保持不变。这只是一个指示,因此如果系统内存不足,则目标页面仍可能被交换。然而,当您指定 LOCK_VM_IN_RAM 标志时,您向内存管理器发出指令,将此页面视为非可分页(即执行驱动程序调用 MmProbeAndLockPages() 以锁定页面所描述的操作),以便该页面保证始终加载在 RAM 中。

编辑:

阅读this


0

一个选择是将主机内存创建为RAM磁盘。虽然分布式Windows代码中不再支持此功能,但您仍然可以免费或通过商业产品获得必要的驱动程序。例如,DRDataRam提供了个人使用的免费驱动程序和商业使用的经许可的产品:http://memory.dataram.com/products-and-services/software/ramdisk

还有ImDisk虚拟驱动程序可用:http://www.ltr-data.se/opencode.html/#ImDisk 它是开源的,可供商业使用。它由Microsoft签名数字证书进行了认证。

有关Windows上RAM驱动器的更多信息,请查看ServerFault.com。


0
你应该看一下地址窗口扩展(AWE)。听起来它似乎符合你所拥有的内存限制(强调我的):

AWE使用物理非分页内存和各种物理内存窗口视图在32位虚拟地址空间内。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接