【问题标题】:Mapping files into virtual memory in C on Windows在 Windows 上将文件映射到 C 中的虚拟内存
【发布时间】:2021-09-22 20:31:32
【问题描述】:

在 POSIX 系统上,我能够使用 mmap 函数比 getline、getc 等更快地读取文件的内容。这在我正在开发的程序中很重要,因为它预计会读取非常大的文件到记忆;使用 getline 迭代收集行的成本太高。可移植性也是我的软件的要求,所以如果我使用 mmap,我需要找到一种使用 WinApi 来存储映射文件的方法,因为我宁愿不通过 cygwin/msys 编译。通过粗略的搜索,我找到了this MSDN 文章,该文章非常简要地描述了一种将文件映射到内存中的方法,但是,从浏览文档中我无法确定如何实际实现它,我被困在查找示例 sn-ps 代码,例如 POSIX mmap。

如何使用 WinApi 的内存映射选项将文件读入 char*?

【问题讨论】:

  • 这是来自 SO 的链接,代码为:stackoverflow.com/questions/22047673/… 它有一个指向 MSDN 文章的链接,但这是另一个 [顶级] 链接:docs.microsoft.com/en-us/windows/win32/memory/file-mapping
  • 可能有一个 [thin] 包装库,根据 win32 API 实现 mmap。也许:docs.microsoft.com/en-us/cpp/c-runtime-library/… 我要做的是假设mmap 可用的代码。如果找不到mmap 包装函数,请自己编写一个,使用上面的win32 API 调用
  • 我还没有看到证据表明从内存映射文件中顺序读取比从 C IO 等缓冲流中顺序读取更快(缺少预取等特殊技术)。您很可能没有将苹果与苹果进行比较。
  • @CraigEstey 就像我说的,这不是将苹果与苹果进行比较。第一个 sn-p 使用 CRT 中的字符串例程,而另一个示例在缓冲区中手动解析字符串。给我看一个例子,当第一个 freads 进入 4k 的缓冲区然后解析它 - 与 mmaping 和解析相比 - 然后它将是苹果对苹果。

标签: c winapi posix mmap


【解决方案1】:

如何使用 WinApi 的内存映射选项将文件读入 字符*?

在 Windows 下,当你在内存中映射一个文件时,你会得到一个指向文件第一个字节被映射的内存位置的指针。您可以将该指针转换为您喜欢的任何数据类型,包括 char*。

换句话说,由 Windows 决定映射数据在内存中的位置。您不能提供 char* 并期望 Windows 会在那里加载数据。

这意味着如果您已经有一个 char* 并且想要该 char* 指向的位置中的文件中的数据,那么您必须复制它。在性能方面不是一个好主意。

这是一个简单的程序,通过将文件映射到内存然后显示所有 ASCII 字符来转储文本文件。使用 MSVC2019 测试。

#include <stdio.h>
#include <Windows.h>

int main(int argc, char *argv[])
{
    TCHAR *lpFileName = TEXT("hello.txt");
    HANDLE hFile;
    HANDLE hMap;
    LPVOID lpBasePtr;
    LARGE_INTEGER liFileSize;

    hFile = CreateFile(lpFileName, 
        GENERIC_READ,                          // dwDesiredAccess
        0,                                     // dwShareMode
        NULL,                                  // lpSecurityAttributes
        OPEN_EXISTING,                         // dwCreationDisposition
        FILE_ATTRIBUTE_NORMAL,                 // dwFlagsAndAttributes
        0);                                    // hTemplateFile
    if (hFile == INVALID_HANDLE_VALUE) {
        fprintf(stderr, "CreateFile failed with error %d\n", GetLastError());
        return 1;
    }

    if (!GetFileSizeEx(hFile, &liFileSize)) {
        fprintf(stderr, "GetFileSize failed with error %d\n", GetLastError());
        CloseHandle(hFile);
        return 1;
    }

    if (liFileSize.QuadPart == 0) {
        fprintf(stderr, "File is empty\n");
        CloseHandle(hFile);
        return 1;
    }

    hMap = CreateFileMapping(
        hFile,
        NULL,                          // Mapping attributes
        PAGE_READONLY,                 // Protection flags
        0,                             // MaximumSizeHigh
        0,                             // MaximumSizeLow
        NULL);                         // Name
    if (hMap == 0) {
        fprintf(stderr, "CreateFileMapping failed with error %d\n", GetLastError());
        CloseHandle(hFile);
        return 1;
    }

    lpBasePtr = MapViewOfFile(
        hMap,
        FILE_MAP_READ,         // dwDesiredAccess
        0,                     // dwFileOffsetHigh
        0,                     // dwFileOffsetLow
        0);                    // dwNumberOfBytesToMap
    if (lpBasePtr == NULL) {
        fprintf(stderr, "MapViewOfFile failed with error %d\n", GetLastError());
        CloseHandle(hMap);
        CloseHandle(hFile);
        return 1;
    }

    // Display file content as ASCII charaters
    char *ptr = (char *)lpBasePtr;
    LONGLONG i = liFileSize.QuadPart;
    while (i-- > 0) {
        fputc(*ptr++, stdout);
    }

    UnmapViewOfFile(lpBasePtr);
    CloseHandle(hMap);
    CloseHandle(hFile);

    printf("\nDone\n");
}

【讨论】:

  • 第一行代码,错误的原因很多(TCHAR *lpFileName = L"hello.txt";)。你肯定知道如何给人留下第一印象。
猜你喜欢
  • 1970-01-01
  • 2015-09-01
  • 1970-01-01
  • 2011-05-23
  • 2011-12-31
  • 1970-01-01
  • 1970-01-01
  • 2015-03-29
  • 1970-01-01
相关资源
最近更新 更多