Windows64下Qt+C++使用CreateFileMapping创建大共享内存失败求助
问题描述
需要通过共享内存在两个进程间传输10M及以上的大图片,但调用CreateFileMappingW时,当BUF_SIZE超过4096字节程序崩溃,后续发现问题出在MapViewOfFile调用上——当该函数最后一个参数设为4097及以上时,程序会卡住。开发环境为Windows 64位系统,使用Qt框架的C++开发。
相关代码如下:
hMapFile = CreateFileMappingW( INVALID_HANDLE_VALUE, // use paging file NULL, // default security PAGE_READWRITE, // read/write access 0, // maximum object size (high-order DWORD) BUF_SIZE, // maximum object size (low-order DWORD)超过4096就不行了,不知道怎么解决 szMapName);
hMapFile = CreateFileMappingW( INVALID_HANDLE_VALUE, // use paging file NULL, // default security PAGE_READWRITE, // read/write access 0, // maximum object size (high-order DWORD) BUF_SIZE, // maximum object size (low-order DWORD) szMapName); // name of mapping object if (hMapFile == NULL){ qDebug() << (TEXT("Could not create file mapping object (%d)."),GetLastError()); return -1; } pBuf = (LPTSTR)MapViewOfFile( hMapFile, // handle to map object FILE_MAP_ALL_ACCESS, // read/write permission 0, 0, 4097);//if this number > 4096, the program will stuck if (pBuf == NULL){ qDebug() << (TEXT("Could not map view of file (%d)."), GetLastError()); CloseHandle(hMapFile); return -1; }
解决方法
1. 正确设置MapViewOfFile的映射大小
MapViewOfFile的最后一个参数指定为0,表示映射整个文件映射对象的全部大小,这是处理大内存映射的标准做法,既能避免手动指定大小带来的页对齐问题,也能适配任意大小的共享内存(包括10M以上的图片)。修改后的调用代码:
pBuf = (BYTE*)MapViewOfFile( hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0); // 映射整个共享内存区域
另外,因为传输的是图片二进制数据,将指针类型转为BYTE*比LPTSTR更合适,避免字符编码相关的潜在问题。
2. 修正错误输出逻辑
原代码中的qDebug()使用了逗号表达式,只会输出GetLastError()的值,错误信息字符串无法正常打印,这会导致排查问题时无法准确判断错误原因。修改为正确的输出方式:
if (hMapFile == NULL){ qDebug() << TEXT("Could not create file mapping object (%d).") << GetLastError(); return -1; }
if (pBuf == NULL){ qDebug() << TEXT("Could not map view of file (%d).") << GetLastError(); CloseHandle(hMapFile); return -1; }
3. 确保BUF_SIZE的正确性
对于10M大小的内存,BUF_SIZE应设置为10 * 1024 * 1024(即10485760),这个值在32位DWORD的范围内(最大为4294967295),因此CreateFileMappingW的高32位参数设为0是正确的。如果后续需要传输更大的文件(超过4GB),则需要将文件大小拆分为高32位和低32位传入。
4. 规避内存页对齐问题
Windows的内存映射是以4KB(4096字节)为一页进行对齐的,手动指定映射大小如果不是页的整数倍,系统会自动向上取整到最近的页边界,但这种操作可能引发意外的内存访问问题。使用0映射整个区域可以完全规避这个问题。
5. 增加进程间同步(可选但关键)
如果两个进程需要读写共享内存,建议配合使用互斥量(CreateMutexW)或事件(CreateEventW)进行同步,避免出现读写冲突导致的程序卡住或数据损坏问题。例如在写入进程写完数据后触发事件,通知读取进程可以开始读取。
内容的提问来源于stack exchange,提问作者vindur

