并不是说它特别有用,但我很好奇为什么以下工作,是因为即使文件被删除后页面仍然在内存中?在这种情况下,如果页面被换出,数据将丢失?
#include <iostream>
#include <memory>
#include <windows.h>
int main()
{
typedef std::unique_ptr<void, decltype(&CloseHandle)> Handle;
typedef std::unique_ptr<void, decltype(&UnmapViewOfFile)> View;
View v(nullptr, UnmapViewOfFile);
{
Handle h(CreateFile(
L"test",
GENERIC_READ | GENERIC_WRITE,
0,
nullptr,
CREATE_ALWAYS,
FILE_FLAG_DELETE_ON_CLOSE,
nullptr
), CloseHandle);
// write something so CreateFileMapping succeeds
DWORD sz;
WriteFile(h.get(), "hello world", 12, &sz, nullptr);
Handle m(CreateFileMapping(
h.get(),
nullptr,
PAGE_READWRITE,
0, 0,
nullptr
), CloseHandle);
v.reset(MapViewOfFile(
m.get(),
FILE_MAP_WRITE,
0, 0,
0
));
char c;
std::cin >> c; // File is still in folder
}
char c;
std::cin >> c; // No file!
std::cout << static_cast<char*>(v.get()); // Still writes
}
答案 0 :(得分:2)
FILE_FLAG_DELETE_ON_CLOSE
遵循不幸的Windows传统,将unlink操作称为“删除”。实际上,该标志仅在文件关闭时才导致文件与指定目录断开连接。
与其他操作系统一样,Windows仅为普通用户代码提供了从特定目录中取消链接文件的能力。删除始终是操作系统的决定,当文件无法以任何方式被引用时发生。
如果你看一下,你会看到文件实际上已从目录中取消链接,但它实际上不会被删除(以及数据在磁盘上可用于重用的空间),直到它的引用计数降到零。映射包含引用。
答案 1 :(得分:2)
FILE_OBJECT
维护一个单独的引用和句柄计数。当最后一个句柄关闭时,文件系统将取消链接该文件并将其切换到“已删除”状态。同时,FILE_OBJECT
在“已删除”状态下可能寿命更长,直到引用计数降至零为止。
引用here:
当您关闭文件句柄时,该文件将被删除。之后,如果 修剪视图中的所有页面并调整其用途,然后进行访问 再次,内存管理器将尝试从文件中读取它们( 现在处于“已删除”状态)。接下来会发生什么取决于 文件系统。 NTFS返回错误代码(
STATUS_END_OF_FILE
),其中 使内存管理器满足页面为零的页面错误。 据我所知,这种行为没有得到记录,所以将来 版本的NTFS(或其他文件系统)可能返回不同的 错误,这将导致页内异常。
这意味着一旦取消链接文件,换出的数据将丢失,并替换为零。
您可以在下面的程序中观察此行为。它执行以下操作:
FILE_FLAG_DELETE_ON_CLOSE
创建一个大小等于RAM数量的文件。0xCD
模式填充文件内容。由于文件的大小与RAM一样大,因此这将迫使页面退出内存并进入文件。输出表明映射开始处的数据全为零-数据已丢失。尚不清楚是否可以将其视为错误,但到今天为止,此行为至少已存在12年,并且不太可能更改。
#include <stdio.h>
#include <stdint.h>
#include <Windows.h>
int main()
{
LARGE_INTEGER size;
MEMORYSTATUSEX mem = { 0 };
mem.dwLength = sizeof mem;
GlobalMemoryStatusEx(&mem);
size.QuadPart = mem.ullTotalPhys;
const HANDLE hFile = CreateFileW(
L"file-under-test",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
fprintf(stderr, "CreateFileW() failed\n");
return 1;
}
if (!SetFilePointerEx(hFile, size, NULL, SEEK_SET)) {
fprintf(stderr, "SetFilePointerEx() failed\n");
return 1;
}
if (!SetEndOfFile(hFile)) {
fprintf(stderr, "SetEndOfFile() failed\n");
return 1;
}
const HANDLE hMap = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
fprintf(stderr, "CreateFileMappingW() failed\n");
return 1;
}
void *const view = MapViewOfFile(
hMap,
FILE_MAP_READ | FILE_MAP_WRITE,
0, 0,
(size_t) size.QuadPart);
if (view == NULL) {
fprintf(stderr, "MapViewOfFile() failed\n");
return 1;
}
CloseHandle(hMap);
CloseHandle(hFile); // this removes the file
memset(view, 0xCD, (size_t) size.QuadPart);
// Print first 16 bytes
for (int i = 0; i < 16; ++i) {
printf("%2d: %#x\n", i, ((const volatile unsigned char *) view)[i]);
}
return 0;
}