Ema*_*ona 4 c memory mapping winapi file
我正在 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);
}
Run Code Online (Sandbox Code Playgroud)
据我所知,只有 MapViewOfFile 需要起始字节与系统粒度对齐,因此我没有费心去修复最大文件映射大小。
我在 1448 KB 文件上尝试了此代码(打印出暗淡的我得到1482159 字节),同时通过 GlobalMemoryStatusEx(&memstatus) 和memstatus.ullAvailVirtual计算可用内存,我得到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 调用
据我所知,MapViewOfFile 只需要起始字节与系统粒度对齐,因此我没有费心去修复最大文件映射大小。
这是错误的根源 - 真的来自MapViewOfFile
dwNumberOfBytesToMap [输入]
要映射到视图的文件映射的字节数。所有字节必须在 CreateFileMapping 指定的最大大小内。如果此参数为 0(零),则映射从指定的偏移量延伸到文件映射的末尾。
如果我们使用0作为MaximumSize,则CreateFileMapping文件映射对象的最大大小等于文件的当前大小。和 :
如果应用程序指定的文件映射对象的大小大于磁盘上实际命名文件的大小,并且页面保护允许写访问(即flProtect 参数指定PAGE_READWRITE或PAGE_EXECUTE_READWRITE),则磁盘上的文件增加以匹配文件映射对象的指定大小。
以及关于GetLastError和 win32 的错误。大多数情况下,错误从内核作为NTSTATUS代码返回。win32层将指定的NTSTATUS代码通过 转换为其等效的系统错误代码RtlNtStatusToDosError。不幸的是,这种转换不是单射的- 许多不同的NTSTATUS代码可以转换为相同的 win32 错误,并且我们在这里丢失了敏感信息。
因此,在某些情况下,更好的调用RtlGetLastNtStatus()而不是GetlastError()- 这会提供更多有关错误的信息。
CreateFileMapping呼叫失败并出现错误代码ERROR_NOT_ENOUGH_MEMORY。
根据错误,ERROR_NOT_ENOUGH_MEMORY我们可以认为系统内存不足(STATUS_NO_MEMORY)。而且还有另一种状态——STATUS_SECTION_TOO_BIG转换为ERROR_NOT_ENOUGH_MEMORY. 当出现以下情况时,返回的外壳CreateFileMapping很薄:ZwCreateSectionSTATUS_SECTION_TOO_BIG
MaximumSize的值太大。当 MaximumSize大于系统定义的节最大值,或者MaximumSize 大于指定文件且该节不可写时,就会发生这种情况。
这正是您的情况:您PAGE_READONLY在调用中使用CreateFileMapping- 所以部分不可写,并且fileMapSize大于指定文件(文件映射对象的大小大于磁盘上实际文件的大小)
MapViewOfFile返回ERROR_ACCESS_DENIED。
又GetLastError()在这里跟我们开了一个残酷的玩笑。初始状态不是STATUS_ACCESS_DENIED我们可以如何等待,而是STATUS_INVALID_VIEW_SIZE。此状态也转换为ERROR_ACCESS_DENIED。MapViewOfFile当不是所有字节都在指定的最大大小内时得到它CreateFileMapping
CreateFileMapping并在循环中多次调用- 这是设计错误 - 只需在循环之前调用此 api 一次。in 循环中只存在 sense call 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();
}
}
Run Code Online (Sandbox Code Playgroud)