如何防止在windows临时删除关闭文件上打开的内存映射刷新到磁盘

如何防止在windows临时删除关闭文件上打开的内存映射刷新到磁盘,windows,winapi,language-agnostic,memory-mapped-files,temporary-files,Windows,Winapi,Language Agnostic,Memory Mapped Files,Temporary Files,更新2/TL;DR 是否有某种方法可以防止windows临时文件中的脏页 由于关闭内存映射而刷新的关闭时删除文件 在这些文件上打开 是。如果在文件初始创建后不需要对文件本身进行任何操作,并且实现了一些命名约定,则可以通过中介绍的策略实现 注意:我仍然很有兴趣找出行为差异如此之大的原因,这取决于贴图的创建方式和处理/取消映射的顺序 我一直在研究进程间共享内存数据结构的一些策略,该结构允许通过使用一系列“内存块”来增加和减少windows上的提交容量 一种可能的方法是使用页面文件支持的命名内存映射

更新2/TL;DR

是否有某种方法可以防止windows临时文件中的脏页 由于关闭内存映射而刷新的关闭时删除文件 在这些文件上打开

是。如果在文件初始创建后不需要对文件本身进行任何操作,并且实现了一些命名约定,则可以通过中介绍的策略实现

注意:我仍然很有兴趣找出行为差异如此之大的原因,这取决于贴图的创建方式和处理/取消映射的顺序


我一直在研究进程间共享内存数据结构的一些策略,该结构允许通过使用一系列“内存块”来增加和减少windows上的提交容量

一种可能的方法是使用页面文件支持的命名内存映射作为块内存。此策略的一个优点是可以使用
SEC_RESERVE
来保留一大块内存地址空间,并使用
VirtualAlloc
MEM_COMMIT
增量分配它。缺点似乎是(a)要求具有
SeCreateGlobalPrivilege
权限,以允许在
Global\
命名空间中使用可共享的名称,以及(b)所有提交的内存都会产生系统提交费用

为了避免这些缺点,我开始研究使用临时文件备份内存映射。也就是说,内存映射到使用
文件_标志_删除_打开|文件_属性_临时
标志组合创建的文件上。这似乎是一种建议的策略,例如,应防止将映射内存刷新到磁盘(除非内存压力导致脏映射页面被调出)

然而,我注意到在拥有进程退出之前关闭映射/文件句柄会导致脏页刷新到磁盘。即使视图/文件句柄不是创建脏页的句柄,并且这些视图/文件句柄是在页面在不同视图中“脏”后打开的,也会发生这种情况

似乎更改处理顺序(即先取消视图映射或先关闭文件句柄)会对启动磁盘刷新的时间产生一些影响,但不会对刷新发生的事实产生影响

因此,我的问题是:

  • 考虑到一个进程/多个进程中的多个线程可能对此类文件具有打开的句柄/视图,是否有某种方法可以使用临时文件备份内存映射,并防止它们在映射/文件关闭时刷新脏页
  • 如果不是,观察到的行为的原因是什么
  • 你知道我可能忽略的另一种策略吗

更新 一些附加信息:当在两个单独(独立)进程中运行下面示例代码的“arena1”和“arena2”部分时,“arena1”是创建共享内存区域的进程,“arena2”是打开共享内存区域的进程,对于具有脏页的映射/块,会观察到以下行为:

  • 如果在“arena1”进程中关闭文件句柄之前的视图,它会在一个(部分)同步进程中将这些块刷新到磁盘上(即,它会将处理线程阻塞几秒钟),独立于“arena2”进程是否启动
  • 如果在查看之前关闭文件句柄,则磁盘刷新只会发生在“arena1”进程中关闭的映射/区块上,而“arena2”进程仍然有一个打开的区块句柄,并且它们看起来是“异步的”,即不阻塞应用程序线程
请参阅下面的(c++)示例代码,该代码允许在我的系统(x64,Win7)上重现该问题:

静态uint64启动;
静态uint64_t经过(){
return::GetTickCount64()-start\u ts;
}
班级竞技场{
公众:
typedef uint8_t*指针;
PageArena(int id,const char*base_name,size_t page_sz,size_t chunk_sz,size_t n_chunks,bool dispose_handle_first):
id(id)、基本名称(基本名称)、pg_sz_(第_sz页)、先处理句柄(先处理句柄){
对于(大小i=0;istd::cout在悬赏期结束后,没有任何能提供更多见解或解决上述问题的答案,我决定更深入地挖掘,并用几种组合和操作序列进行更多的实验

因此,我相信我已经找到了一种方法来实现进程之间的内存映射共享,而不是临时的、关闭时删除的文件,这些文件在关闭时不会刷新到磁盘上

基本思想涉及在新创建临时文件时创建内存映射,该临时文件的映射名可用于调用
OpenFileMapping

// build a unique map name from the file name.
auto map_name = make_map_name(file_name); 

// Open or create the mapped file.
auto mh = ::OpenFileMappingA(FILE_MAP_ALL_ACCESS, false, map_name.c_str());
if (mh == 0 || mh == INVALID_HANDLE_VALUE) {
    // existing map could not be opened, create the file.
    auto fh = ::CreateFileA(name.c_str(), kReadWrite, kFileSharing, nullptr, CREATE_NEW, kTempFlags, 0);
    if (fh != INVALID_HANDLE_VALUE) {
        // set its size.
        LARGE_INTEGER newpos;
        newpos.QuadPart = desired_size;
        ::SetFilePointerEx(fh, newpos, 0, FILE_BEGIN);
        ::SetEndOfFile(fh);
        // create the map
        mh = ::CreateFileMappingA(mh, nullptr, PAGE_READWRITE, 0, 0, map_name.c_str());
        // close the file handle
        // from now on there will be no accesses using file handles.
        ::CloseHandle(fh);
    }
}
因此,文件句柄仅在新创建文件时使用,并在创建映射后立即关闭,而映射句柄本身保持打开状态,以允许在不需要访问文件句柄的情况下打开映射。请注意,此处存在争用条件,我们需要在任何“真实代码”中处理该争用条件(以及添加适当的错误检查和处理)

因此,如果我们获得了有效的地图句柄,我们可以创建视图

auto map_ptr = MapViewOfFile(mh, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (map_ptr) {
    // determine its size.
    MEMORY_BASIC_INFORMATION mbi;
    if (::VirtualQuery(map_ptr, &mbi, sizeof(MEMORY_BASIC_INFORMATION)) > 0) 
        map_size = mbi.RegionSize;
}
一段时间后关闭映射文件时:在取消映射视图之前关闭映射句柄:

if (mh == 0 || mh == INVALID_HANDLE_VALUE) {
    ::CloseHandle(mh);
    mh = INVALID_HANDLE_VALUE;
}
if (map_ptr) {
    ::UnmapViewOfFile(map_ptr);
    map_ptr = 0;
    map_size = 0;
}
根据我到目前为止所做的测试
if (mh == 0 || mh == INVALID_HANDLE_VALUE) {
    ::CloseHandle(mh);
    mh = INVALID_HANDLE_VALUE;
}
if (map_ptr) {
    ::UnmapViewOfFile(map_ptr);
    map_ptr = 0;
    map_size = 0;
}
char base_path[MAX_PATH];
GetTempPathA(MAX_PATH, base_path);
strcat_s(base_path, MAX_PATH, "page_pool");