问题描述
我正在编写一个程序,它使用文件映射从文件中注销。
当我想读取日志时,调用 Logger::Destroy()
以便将写入文件映射视图的内容刷新到物理文件。
代码如下:
int Logger::Destroy() {
if (m_lpMapAddress) {
auto bRet = UnmapViewOfFile(m_lpMapAddress);
// succeed
}
if (m_hMapFile) {
auto bRet = CloseHandle(m_hMapFile);
// succeed
m_hMapFile = NULL;
}
int nSize = m_lpCurAddress - m_lpMapAddress;
if (nSize > 0
&& nSize < (1024 * 1024 * 16 * 2))
{
DWORD dwPtr = SetFilePointer(m_hFile,nSize,FILE_BEGIN);
///// Succeed
// if (dwPtr == INVALID_SET_FILE_POINTER)
// DbgViewOut(__FUNCTION__ " SetFilePointer error: %d \n",GetLastError());
//// Error occurs : "SetEndOfFile returned : 0 1224"
BOOL bRet = SetEndOfFile(m_hFile);
DbgViewOut(__FUNCTION__ " SetEndOfFile returned : %d %d\n",bRet,GetLastError());
....
}
m_lpMapAddress = m_lpCurAddress = NULL;
return 0;
}
问题是 SetEndOfFile()
以 ERROR_USER_MAPPED_FILE
失败,即使 CloseHandle(m_hMapFile)
成功。
所以我用谷歌搜索了微软关于文件映射的手册,并对其中的一些进行了评论。
https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
即使文件视图仍然打开,关闭文件映射句柄也能成功。有关更多信息,请参阅关闭文件映射对象。 >
https://docs.microsoft.com/en-us/windows/win32/memory/closing-a-file-mapping-object
当每个进程完成使用文件映射对象并取消映射所有视图时,它必须通过调用 CloseHandle 关闭文件映射对象的句柄和磁盘上的文件。 即使存在仍然打开的文件视图,对 CloseHandle 的这些调用也会成功。但是,保留文件视图映射会导致内存泄漏。
它说我无法相信 CloseHandle()
的结果。
我找不到解决方案。
谁能帮帮我?谢谢。
补充:我在Logger::Destroy()
中调用了std::lock_guard<std::mutex>
,这会不会影响到麻烦?
:我已经测试过了,锁定不会影响它。
更新:我已阅读When error 1224: ERROR_USER_MAPPED_FILE occurs?。而且我认为没有太大的区别。
我附加了初始化文件映射的 Logger::Initialize()
代码。
而且,Logger::Initialize()
和 Logger::Destroy()
都在同一个进程和线程中,不需要与其他人共享。
int Logger::Initialize()
{
m_hFile = CreateFileA(
m_zFileName.c_str(),GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_norMAL,NULL);
if (m_hFile == INVALID_HANDLE_VALUE) {
DbgViewOut(__FUNCTION__ " CreateFileA error: %d \n",GetLastError());
return -1;
}
m_hMapFile = CreateFileMappingA(
m_hFile,PAGE_READWRITE,1024 * 1024 * 16 * 2,m_zMapKey.c_str());
if (!m_hMapFile) {
DbgViewOut(__FUNCTION__ " CreateFileMapping error: %d \n",GetLastError());
return -1;
}
m_hMapFile = OpenFileMappingA(FILE_MAP_WRITE,TRUE,m_zMapKey.c_str());
if (m_hMapFile == NULL) {
DbgViewOut(__FUNCTION__ " OpenFileMapping error: %d \n",GetLastError());
return -1;
}
m_lpMapAddress = (BYTE*)MapViewOfFile(
m_hMapFile // handle to mapping object,FILE_MAP_ALL_ACCESS // read/write,0 // high-order 32 bits of file offset,0 // low-order 32 bits of file offset,0); // number of bytes
if (m_lpMapAddress == NULL) {
DbgViewOut(__FUNCTION__ " MapViewOfFile error: %d \n",GetLastError());
return -1;
}
m_lpCurAddress = m_lpMapAddress;
return 0;
}
解决方法
我会回答我自己的问题。
问题是我在OpenFileMappingA()
之后调用了CreateFileMappingA()
,所以m_hMapFile
返回的CreateFileMappingA()
泄漏了。
我删除了 OpenFileMappingA()
,问题就消失了。
我曾经知道它必须在创建句柄后打开一次,几个月前我曾经在进程之间的内存共享中这样做。 但是在这次争论中,我意识到创建后打开句柄是不必要的。
非常感谢@RemyLebeau 的详细建议。
int Logger::Initialize()
{
m_hFile = CreateFileA(
m_zFileName.c_str(),GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
if (m_hFile == INVALID_HANDLE_VALUE) {
DbgViewOut(__FUNCTION__ " CreateFileA error: %d \n",GetLastError());
return -1;
}
m_hMapFile = CreateFileMappingA(
m_hFile,PAGE_READWRITE,LINM_LOGGER_FILE_MAXSIZE * 2,m_zMapKey.c_str());
if (!m_hMapFile) {
DbgViewOut(__FUNCTION__ " CreateFileMapping error: %d \n",GetLastError());
return -1;
}
//// This was the solution - CreateFileMappingA & OpenFileMappingA mustn't be used for same process or thread.
//
// m_hMapFile = OpenFileMappingA(FILE_MAP_WRITE,TRUE,m_zMapKey.c_str());
// if (m_hMapFile == NULL) {
// DbgViewOut(__FUNCTION__ " OpenFileMapping error: %d \n",GetLastError());
// return -1;
// }
m_lpMapAddress = (BYTE*)MapViewOfFile(
m_hMapFile // handle to mapping object,FILE_MAP_ALL_ACCESS // read/write,0 // high-order 32 bits of file offset,0 // low-order 32 bits of file offset,0); // number of bytes
if (m_lpMapAddress == NULL) {
DbgViewOut(__FUNCTION__ " MapViewOfFile error: %d \n",GetLastError());
return -1;
}
m_lpCurAddress = m_lpMapAddress;
return 0;
}