如何防止在windows临时删除关闭文件上打开的内存映射刷新到磁盘
更新2/TL;DR 是否有某种方法可以防止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上的提交容量 一种可能的方法是使用页面文件支持的命名内存映射
我一直在研究进程间共享内存数据结构的一些策略,该结构允许通过使用一系列“内存块”来增加和减少windows上的提交容量 一种可能的方法是使用页面文件支持的命名内存映射作为块内存。此策略的一个优点是可以使用
SEC_RESERVE
来保留一大块内存地址空间,并使用VirtualAlloc
和MEM_COMMIT
增量分配它。缺点似乎是(a)要求具有SeCreateGlobalPrivilege
权限,以允许在Global\
命名空间中使用可共享的名称,以及(b)所有提交的内存都会产生系统提交费用
为了避免这些缺点,我开始研究使用临时文件备份内存映射。也就是说,内存映射到使用文件_标志_删除_打开|文件_属性_临时
标志组合创建的文件上。这似乎是一种建议的策略,例如,应防止将映射内存刷新到磁盘(除非内存压力导致脏映射页面被调出)
然而,我注意到在拥有进程退出之前关闭映射/文件句柄会导致脏页刷新到磁盘。即使视图/文件句柄不是创建脏页的句柄,并且这些视图/文件句柄是在页面在不同视图中“脏”后打开的,也会发生这种情况
似乎更改处理顺序(即先取消视图映射或先关闭文件句柄)会对启动磁盘刷新的时间产生一些影响,但不会对刷新发生的事实产生影响
因此,我的问题是:
- 考虑到一个进程/多个进程中的多个线程可能对此类文件具有打开的句柄/视图,是否有某种方法可以使用临时文件备份内存映射,并防止它们在映射/文件关闭时刷新脏页
- 如果不是,观察到的行为的原因是什么
- 你知道我可能忽略的另一种策略吗
更新 一些附加信息:当在两个单独(独立)进程中运行下面示例代码的“arena1”和“arena2”部分时,“arena1”是创建共享内存区域的进程,“arena2”是打开共享内存区域的进程,对于具有脏页的映射/块,会观察到以下行为:
- 如果在“arena1”进程中关闭文件句柄之前的视图,它会在一个(部分)同步进程中将这些块刷新到磁盘上(即,它会将处理线程阻塞几秒钟),独立于“arena2”进程是否启动
- 如果在查看之前关闭文件句柄,则磁盘刷新只会发生在“arena1”进程中关闭的映射/区块上,而“arena2”进程仍然有一个打开的区块句柄,并且它们看起来是“异步的”,即不阻塞应用程序线程
静态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;i std::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");