【问题标题】:WINAPI C - CreateFileMapping fails with error 8 - ERROR_NOT_ENOUGH_MEMORYWINAPI C - CreateFileMapping 失败并出现错误 8 - ERROR_NOT_ENOUGH_MEMORY
【发布时间】:2018-02-11 07:13:57
【问题描述】:

我正在使用 Windows 上的文件映射,但遇到了一些问题。 首先,我需要部分映射文件并动态设置文件的开头和结尾。

我的代码如下:

long fiveMB = 5 * pow(2, 20);
for(int i=0;i<parts;i++){
    long start = (i)*fiveMB;
    long end = (i + 1)*fiveMB;
    long realEnd = end;
    if (roundedDim<realEnd)
        realEnd = dim;

    long chunkDim = realEnd - start;
    LARGE_INTEGER fileMapStart.QuadPart = (start/granularity)*granularity;
    LARGE_INTEGER mapViewSize.QuadPart = (start%granularity) + chunkDim;
    LARGE_INTEGER fileMapSize.QuadPart = start + chunkDim;
    long offset = start - fileMapStart.QuadPart;

    HANDLE fileMappingH= CreateFileMapping(fileH, NULL, PAGE_READONLY, fileMapSize.HighPart, fileMapSize.LowPart, NULL);

    if(fileMappingH == INVALID_HANDLE_VALUE || fileMappingH == NULL){
       printf("Error mapping file: %d\n",GetLastError());
       CloseHandle(fileH);
       return 1;
    }

    char *mapView = (char *)MapViewOfFile(fileMappingH, FILE_MAP_READ, fileMapStart.HighPart, fileMapStart.LowPart, mapViewSize.QuadPart);
    if ((LPVOID)mapView == NULL) {
        printf("Error mapView: %d\n", GetLastError());
        CloseHandle(fileMappingH);
        CloseHandle(file);
        return 1;
    }

    mapView += offset;

    /* doing all the stuff */

    UnmapViewOfFile((LPVOID)mapView);
    CloseHandle(fileMappingH);
}

据我所知,只有 MapViewOfFile 要求起始字节与系统粒度对齐,所以我没有费心为此修复最大文件映射大小。

我在通过 GlobalMemoryStatusEx(&memstatus) 和 memstatus.ullAvailVirtual 计算可用内存时在 1448 KB 文件上尝试了此代码(打印出暗淡我得到 1482159 字节)我得到 2092208128 字节,但仍然坚持 CreateFileMapping 调用失败,错误代码为 8,ERROR_NOT_ENOUGH_MEMORY

我也尝试调用 CreateFileMapping(fileH, NULL, PAGE_READONLY, 0, 0, NULL) 来对整个文件进行内存映射,但是 MapViewOfFile 出现了问题,错误 5,ERROR_ACCESS_DENIED

我不明白我在这里做错了什么,因为我在同一个项目的 Linux 版本上使用 mmap 成功地做到了。

感谢任何可能提供帮助的人。


编辑:

  • c 是剩菜,我的意思是我

  • 添加了 UnmapViewOfFile 和 CloseHandle 调用

【问题讨论】:

  • c 未定义。
  • 一开始 - 在循环中调用 CreateFileMapping - 这是设计错误。你只需要调用一次。在第二个错误 CreateFileMapping 返回 0 但不是 INVALID_HANDLE_VALUE - 没有意义检查 INVALID_HANDLE_VALUE
  • 关于错误ERROR_NOT_ENOUGH_MEMORY - 几个 ntstatus 代码被转换为这个错误。所以这里在win32层上丢失了。需要调用RtlGetLastNtStatus() - 可能是你得到例如STATUS_SECTION_TOO_BIG - 这意味着你设置的部分大小超过文件大小,因为你使用PAGE_READONLY文件不能扩展
  • @ifelsemonkey 文件映射使用的唯一 RAM 是 MapViewOfFile 调用指定的 RAM;因此,如果您知道哪个是正确的文件偏移量以及要从那里检查多少字节,您只需正确指定MapViewOfFile 的最后一个参数即可,在我的代码中为mapViewSize.QuadPartHere你可以看到整个代码,链接到奇特的MapViewOfFile函数调用
  • @ifelsemonkey 是的,但是在CreateFileMapping 中您必须输入整个文件大小;为了检查给定偏移量的 3 个字节,必须通过 MapViewOfFile 指定:请注意,此类调用中涉及的每个大小都必须与文件系统粒度对齐!因此,如果要读取这 3 个字节,则必须映射以预期粒度正确开始的文件块,大小为 3 个字节,然后考虑偏移量以本地化此类字节

标签: c file winapi memory mapping


【解决方案1】:

据我所知,MapViewOfFile 只需要起始字节为 与系统粒度保持一致,所以我没有费心去修复 最大文件映射大小。

这是错误的根源——真的来自MapViewOfFile

dwNumberOfBytesToMap [in]

要映射到视图的文件映射的字节数。 所有字节 必须在 CreateFileMapping 指定的最大大小内。如果 此参数为 0(零),映射从指定的扩展 到文件映射末尾的偏移量。

如果我们在CreateFileMapping 中使用0 作为MaximumSize,则文件映射对象的最大大小等于文件的当前大小。和:

如果应用程序指定文件映射对象的大小 大于磁盘上实际命名文件的大小,并且 如果 页面保护允许写访问(即,flProtect 参数指定 PAGE_READWRITEPAGE_EXECUTE_READWRITE), 然后增加磁盘上的文件以匹配指定的大小 文件映射对象。

还有关于 GetLastError 和 win32 的错误。大多数情况下,错误从内核返回为NTSTATUS 代码。 win32 层通过RtlNtStatusToDosError 将指定的NTSTATUS 代码转换为其等效的系统错误代码。不幸的是,这种转换不是单射的 - 许多不同的 NTSTATUS 代码可以转换为相同的 win32 错误,我们在这里丢失了敏感信息。

所以在某些情况下调用RtlGetLastNtStatus() 而不是GetlastError() 更好——这可以提供更多关于错误的信息。

CreateFileMapping 调用失败并带有错误代码 ERROR_NOT_ENOUGH_MEMORY.

基于错误ERROR_NOT_ENOUGH_MEMORY,我们可以认为系统内存不足(STATUS_NO_MEMORY)。而且还有另一种状态——STATUS_SECTION_TOO_BIG 转换为ERROR_NOT_ENOUGH_MEMORYCreateFileMappingZwCreateSection 之上的薄壳 STATUS_SECTION_TOO_BIG 在以下情况下返回:

MaximumSize 的值太大。发生这种情况时 MaximumSize 大于系统定义的节的最大值,或者如果 MaximumSize 大于指定的文件并且 部分不可写

这正是你的情况:你在调用 CreateFileMapping 时使用 PAGE_READONLY - 所以 section 不可写 并且 fileMapSize 大于指定的文件(大小对于大于磁盘上实际文件大小的文件映射对象)

MapViewOfFile返回ERROR_ACCESS_DENIED

GetLastError() 再次在这里与我们开一个残酷的笑话。初始状态不是STATUS_ACCESS_DENIED我们如何等待,而是STATUS_INVALID_VIEW_SIZE。此状态也转换为ERROR_ACCESS_DENIED。当不是CreateFileMapping指定的最大大小内的所有字节都不是MapViewOfFile时得到它

并在循环中多次调用CreateFileMapping - 这是设计错误 - 只需在循环之前调用此 api 一次。在循环中只存在感知调用MapViewOfFile。测试代码可以是:

void TestMap(PCWSTR lpFileName, ULONG dwChunkSize)
{
    HANDLE hFile = CreateFileW(lpFileName, FILE_GENERIC_READ, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);

    if (hFile != INVALID_HANDLE_VALUE)
    {
        FILE_STANDARD_INFO fsi;
        if (GetFileInformationByHandleEx(hFile, FileStandardInfo, &fsi, sizeof(fsi)))
        {
            if (HANDLE hSection = CreateFileMappingW(hFile, 0, PAGE_READONLY, 0, 0, 0))
            {
                if (ULONG n = (ULONG)((fsi.EndOfFile.QuadPart + (dwChunkSize - 1)) / dwChunkSize))
                {
                    LARGE_INTEGER ofs = {};
                    do 
                    {
                        if (PVOID pv = MapViewOfFile(hSection, FILE_MAP_READ, ofs.HighPart, ofs.LowPart, --n ? dwChunkSize : 0))
                        {
                            UnmapViewOfFile(pv);
                        }
                        else
                        {
                            RtlGetLastNtStatus();
                        }
                    } while (ofs.QuadPart += dwChunkSize, n);
                }

                CloseHandle(hSection);
            }
            else
            {
                RtlGetLastNtStatus();
            }
        }
        CloseHandle(hFile);
    }
    else
    {
        RtlGetLastNtStatus();
    }
}

【讨论】:

  • 我犯的唯一错误是将文件尺寸四舍五入,这将导致CreateFileMapping()MapViewOfFile() 的失败我什至阅读了很多次该文档页面,但我仍然不会弄清楚 WINAPI 对此有多么严格,而 POSIX 让我可以毫无困难地做到这一点,感谢所有的努力!
  • 优秀的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 2015-12-18
  • 1970-01-01
  • 2018-02-24
  • 2012-09-24
  • 1970-01-01
  • 2013-07-18
相关资源
最近更新 更多