CreateFileMapping,MapViewOfFile,如何避免占用系统内存

Posted

技术标签:

【中文标题】CreateFileMapping,MapViewOfFile,如何避免占用系统内存【英文标题】:CreateFileMapping, MapViewOfFile, how to avoid holding up the system memory 【发布时间】:2010-12-25 05:47:29 【问题描述】:

我正在开发一个针对可能只有 256MB RAM(Windows 2000 及更高版本)的桌面系统的应用程序。在我的应用程序中,我有这个大文件 (>256MB),其中包含大约 160 字节/每个的固定记录。这个应用程序有一个相当长的过程,随着时间的推移,它将随机访问大约 90% 的文件(用于读取和写入)。任何给定的记录写入与读取该特定记录的距离不会超过 1,000 次记录访问(我可以调整此值)。

对于这个过程,我有两个明显的选择:常规 I/O(FileRead、FileWrite)和内存映射(CreateFileMapping、MapViewOfFile)。后者在具有足够内存的系统中应该更有效,但在内存不足的系统中,它将换出大部分其他应用程序的内存,这在我的应用程序中是禁止的。有没有办法防止进程耗尽所有内存(例如,强制刷新我不再访问的内存页面)?如果这不可能,那么我必须求助于常规 I/O;我本来希望在写作部分使用重叠 I/O(因为访问是如此随机),但文档说 writes of less than 64K are always served synchronously。

欢迎任何改进 I/O 的想法。

【问题讨论】:

也许 VirtualFree(MEM_DECOMMIT) 可以提供帮助?我不熟悉它。 否,对于 MMF,VirtualFree(MEM_DECOMMIT) 失败;我刚刚检查过。 我们输入CreateFileMapping 的文件偏移参数是否转换为map object 消耗的RAM 量?我不太明白为什么这个偏移量会变成map object 的大小。我们对这个偏移量之前的字节不感兴趣。 (除了一些颗粒状的小碎片。 【参考方案1】:

您是否使用 MapViewOfFile 将整个文件映射为一个块?如果是,请尝试映射较小的部分。您可以使用 FlushViewOfFile() 刷新视图

【讨论】:

我不按顺序访问文件;我无法控制访问文件的随机模式,因此映射其中的一小部分将非常无效。此外, FlushViewOfFile() 不会释放任何内存;只强制写入脏页。【参考方案2】:

只需将整个文件映射到内存即可。这会消耗虚拟内存而不是物理内存。该文件从磁盘分段读取,并通过管理交换文件的相同策略从内存中逐出。

【讨论】:

嗯,这正是问题所在。管理交换文件的策略倾向于将内容保留在内存中,而不是文件缓存中的内容,因此访问映射文件最终将换出大部分其他进程。【参考方案3】:

我终于找到了方法,源自一个线程here。诀窍是在我需要取消提交的范围上使用 VirtualUnlock();尽管此函数返回 FALSE 并返回错误 0x9e(“段已解锁”),但实际上已释放内存,即使页面已被修改(文件已正确更新)。

这是我的示例测试程序:

#include "stdafx.h"

void getenter(void)

    int     ch;
    for(;;)
    
        ch = getch();
        if( ch == '\n' || ch == '\r' ) return;
    


int main(int argc, char* argv[])

    char*   fname = "c:\\temp\\MMFTest\\TestFile.rar";      // 54 MB
    HANDLE  hfile = CreateFile( fname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL );
    if( hfile == INVALID_HANDLE_VALUE )
    
        fprintf( stderr, "CreateFile() error 0x%08x\n", GetLastError() );
        getenter();
        return 1;
    

    HANDLE map_handle = CreateFileMapping( hfile, NULL, PAGE_READWRITE | SEC_RESERVE, 0, 0, 0);
    if( map_handle == NULL )
    
        fprintf( stderr, "CreateFileMapping() error 0x%08x\n", GetLastError() );
        getenter();
        CloseHandle(hfile);
        return 1;
    

    char* map_ptr = (char*) MapViewOfFile( map_handle, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0 );
    if( map_ptr == NULL )
    
        fprintf( stderr, "MapViewOfFile() error 0x%08x\n", GetLastError() );
        getenter();
        CloseHandle(map_handle);
        CloseHandle(hfile);
        return 1;
    

    // Memory usage here is 704KB
    printf("Mapped.\n"); getenter();

    for( int n = 0 ; n < 10000 ; n++ )
    
        map_ptr[n*4096]++;
    

    // Memory usage here is ~40MB
    printf("Used.\n"); getenter();

    if( !VirtualUnlock( map_ptr, 5000 * 4096 ) )
    
        // Memory usage here is ~20MB
        // 20MB already freed!
        fprintf( stderr, "VirtualUnlock() error 0x%08x\n", GetLastError() );
        getenter();
        UnmapViewOfFile(map_ptr);
        CloseHandle(map_handle);
        CloseHandle(hfile);
        return 1;
    

    // Code never reached
    printf("VirtualUnlock() executed.\n"); getenter();

    UnmapViewOfFile(map_ptr);
    CloseHandle(map_handle);
    CloseHandle(hfile);

    printf("Unmapped and closed.\n"); getenter();

    return 0;

如您所见,执行 VirtualUnlock() 后程序的工作集减少了,正如我所需要的。我只需要跟踪我更改的页面,以便在适当的时候解锁。

【讨论】:

太好了,谢谢分享。我一直在拼命寻找一种方法来控制内存映射文件使用的内存量,但我已经失去了实现它的希望。我只希望 MS 不会禁用这是一些未来的操作系统。毕竟,为什么我们不能根据需要简单地提交和取消提交页面? 注意:您描述的错误代码似乎是基于 VirtualUnlock 文档注释的预期:“如果指定范围内的任何页面未锁定,VirtualUnlock 从工作集中删除此类页面,设置最后错误到 ERROR_NOT_LOCKED,并返回 FALSE。" 还有一点需要注意:这个实验有些误导。进程的工作集减少了,但这并不一定意味着页面被丢弃。如果您再次映射,您可以看到您立即获得它,无需任何页面文件活动。在***.com/questions/3525202/… 中查看我的实验【参考方案4】:

VirtualUnlock 似乎不起作用。你需要做的是打电话 FlushViewOfFile(map_ptr,0) 紧接在 UnmapViewOfFile(map_ptr) 之前。 Windows 任务管理器不会显示物理内存使用情况。使用 SysInternals 的 ProcessExplorer

【讨论】:

以上是关于CreateFileMapping,MapViewOfFile,如何避免占用系统内存的主要内容,如果未能解决你的问题,请参考以下文章

CreateFileMapping,MapViewOfFile,如何避免占用系统内存

CreateFileMapping() 用于写入文件长度未知的文本

无法释放 C++ 中 CreateFileMapping 和 MapViewOfFile 创建的共享内存

CreateFileMapping MapViewOfFile UnmapViewOfFile函数用法及示例

尝试使用 CreateFileMapping 和自定义 DACL 创建只读共享内存区域在 OpenFileMapping 中失败

Windows API一日一练(59)CreateFileMapping和MapViewOfFile函数