了解内存映射中的稀疏文件行为

问题描述 投票:0回答:1

我正在研究稀疏文件的内存映射操作。我注意到,一旦发出写操作,它至少会占用 64KB 的空间。当我写入逻辑地址中的下一个 64KB 区域时,文件大小增加 64KB,使总文件大小为 128KB。我对此行为感到困惑,因为即使我只向逻辑地址 0 和 65536 写入 1 个字节的数据,稀疏文件仍然占用 128KB 的空间。

我的问题是:这是使用稀疏文件的正确方法吗? 64KB 增长是 Windows 中的设置吗?另外,如果我先对高逻辑地址执行写入操作(例如写入512KB位置),然后写入0KB逻辑地址,文件会报告大小为512KB还是会占用128KB作为两个64KB块?

这是我的测试代码:

#include <iostream>
#include <windows.h>
#include <fileapi.h>
using namespace std;

const char* FILENAME = "sparse_test.txt";
HANDLE hFile = nullptr;

void writeSparse(BYTE* data, int n, int offset) {
    for (size_t i = 0; i < 1024 * n; i += offset) {
        data[i] = 1;
        cout << "Wrote byte " << i / 1024 + 1 << " at position " << i << "\n";
    }
}

void writeSequential(BYTE* data, int n, int offset) {
    for (size_t i = 0; i < 1024 * n; i += offset) {
        memset(data + i, '2', offset);
        cout << "Wrote byte 2 at position " << offset * i << "-" << (i + 1) * offset << "\n";
    }
}

int main(int argc, char* argv[]) {
    if (argc != 4) {
        cerr << "Usage: " << argv[0] << " <mode 1 or 2>\n";
        return 1;
    }

    hFile = CreateFileA(FILENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL,
                        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    DWORD dwTemp;
    BOOL bSparse = DeviceIoControl(hFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dwTemp, NULL);
    HANDLE hMapping = CreateFileMappingA(hFile, NULL, PAGE_READWRITE, 0, 1024 * 1024, NULL);
    BYTE* data = (BYTE*)MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);

    if (data == NULL) {
        cerr << "Mapping failed: " << GetLastError() << "\n";
        return 1;
    }

    (stoi(argv[1]) == 1) ? writeSparse(data, stoi(argv[2]), stoi(argv[3]) * 1024) : writeSequential(data, stoi(argv[2]), stoi(argv[3]) * 1024);

    UnmapViewOfFile(data);
    CloseHandle(hMapping);
    CloseHandle(hFile);

    return 0;
}

如何使用此代码:

Compile the code into an executable, e.g., test.exe.

Run the executable with three arguments:
    The first argument is the mode: 1 for scattered writes (writing the first byte in each designated interval) or 2 for sequential writes (writing all bytes in the specified jump interval).
    The second argument represents the maximum amount of data to be written, in kilobytes (KB).
    The third argument is the offset for each write operation, in kilobytes (KB).

For example:
    For scattered writing: test.exe 1 128 4 means to write the first byte in each jump interval, with a jump of 4KB, up to a maximum of 128KB.
    For sequential writing: test.exe 2 128 4 means to write all bytes in each jump interval, with a jump of 4KB.
c++ windows winapi
1个回答
0
投票

内存映射 IO 只能以页面大小的粒度(通常为 4 kiB)跟踪内存的使用情况。操作系统无法拦截每个单字节 IO。它唯一跟踪的是您第一次读取或写入页面的时间。然后,这会导致操作系统从磁盘加载现有数据(如果存在)或分配新的零填充页面。对于写入,它还会将页面标记为脏页面,以便稍后将更改刷新到磁盘。

据我了解,由于历史原因,Windows 使用了更大的 64 kiB 粒度。 Raymond Chen 对此进行了解释。

另一个原因可能是文件系统的分配粒度。许多文件系统可以使用不同的块/簇大小进行格式化。

所有这些都依赖于平台并且可能会发生变化。例如,有人讨论增加 ARM 上的默认页面大小。

© www.soinside.com 2019 - 2024. All rights reserved.