CreateFileMapping、MapViewOfFile、处理泄漏的 c++
CreateFileMapping, MapViewOfFile, handle leaking c++
背景:我正在尝试创建一个可以被多个进程访问的内存映射文件。在下面的代码中,我只放入了与我目前必须使事情变得更简单的问题有关的代码。根据 msdn,我应该能够创建一个文件映射,映射文件的视图并关闭我从 CreateFileMapping 收到的句柄,而 MapViewOfFile 将使我的 FileMap 保持活动状态。在我 UnmapViewOfFile 之前,FileMap 应该仍然可以访问。
MSDN: CreateFileMapping function
Mapped views of a file mapping object maintain internal references to the object, and a file mapping object does not close until all references to it are released. Therefore, to fully close a file mapping object, an application must unmap all mapped views of the file mapping object by calling UnmapViewOfFile and close the file mapping object handle by calling CloseHandle. These functions can be called in any order.
问题: 在成功映射文件视图然后关闭 CreateFileMapping 接收到的句柄后,FileMap 不再存在(它应该仍然存在)并且我的 MemMapFileReader 能够创建一个错误为 0 的新地图。(当它应该收到错误 183 'already exists')
错误的解决方案:不关闭句柄允许 MemMapFileReader 程序访问它,但会导致 MemMapFileCreator 中的句柄泄漏,因为在进程关闭之前句柄永远不会关闭.
问题:我错过了什么或做错了什么?
MemMapFileCreator
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <iostream>
#define BUF_SIZE 256
TCHAR szName[] = TEXT("MyFileMappingObject");
TCHAR szMsg[] = TEXT("Message from first process.");
int _tmain()
{
HANDLE hMapFile;
LPCTSTR pBuf;
hMapFile = CreateFileMapping(
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)
szName); // name of mapping object
DWORD lastError = GetLastError();
if (hMapFile == NULL)
{
_tprintf(TEXT("Could not create file mapping object (%d).\n"),
GetLastError());
std::cin.get();
return 1;
}
pBuf = (LPTSTR)MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (pBuf == NULL)
{
_tprintf(TEXT("Could not map view of file (%d).\n"),
GetLastError());
CloseHandle(hMapFile);
std::cin.get();
return 1;
}
CopyMemory((PVOID)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR)));
CloseHandle(hMapFile);
_getch();
UnmapViewOfFile(pBuf);
return 0;
}
MemMapFileReader
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <iostream>
#pragma comment(lib, "user32.lib")
#define BUF_SIZE 256
TCHAR szName[] = TEXT("MyFileMappingObject");
int _tmain()
{
HANDLE hMapFile;
LPCTSTR pBuf;
hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE, // read/write access
0,
BUF_SIZE,
szName); // name of mapping object
DWORD lastError = GetLastError();
if (hMapFile == NULL)
{
_tprintf(TEXT("Could not open file mapping object (%d).\n"),
GetLastError());
std::cin.get();
return 1;
}
pBuf = (LPTSTR)MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (pBuf == NULL)
{
_tprintf(TEXT("Could not map view of file (%d).\n"),
GetLastError());
CloseHandle(hMapFile);
std::cin.get();
return 1;
}
MessageBox(NULL, pBuf, TEXT("Process2"), MB_OK);
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
std::cin.get();
return 0;
}
来自 MSDN:
Mapped views of a file mapping object maintain internal references to the object, and a file mapping object does not close until all references to it are released. Therefore, to fully close a file mapping object, an application must unmap all mapped views of the file mapping object by calling UnmapViewOfFile and close the file mapping object handle by calling CloseHandle. These functions can be called in any order.
CreateFileMapping 文档说要完全关闭文件,必须关闭所有句柄,顺序无关紧要。此逻辑不可逆:您不能关闭句柄并期望使用其他句柄,就好像文件映射不是 'closed'.
也就是说,清理文件映射需要按任意顺序关闭所有句柄。但是,您不能关闭底层文件映射对象并仍然使用依赖于它的视图。
CreateFileMapping()
文档说:
Mapped views of a file mapping object maintain internal references to the object, and a file mapping object does not close until all references to it are released.
CloseHandle()
文档说:
In general, CloseHandle invalidates the specified object handle, decrements the object's handle count, and performs object retention checks. After the last handle to an object is closed, the object is removed from the system.
映射视图只是将映射对象的引用计数保持在零以上,直到它们被取消映射,但它们不会保持底层 file/mapping 本身打开。
在您关闭创建者进程中的节句柄视图后不会消失,直到取消映射 bat 它的名称 - "MyFileMappingObject" 在 NT 命名空间中被销毁。结果下次调用 CreateFileMapping - 未找到命名对象 "MyFileMappingObject" 并创建新的(当您的逻辑打开现有的桅杆时)。再次 - 部分没有被破坏,但它的名字被破坏了。
你的名字 - 糟糕的解决方案 - 真的不错 - 这绝对正常 - 你不能关闭部分的句柄。这不是句柄泄漏 - 只是在您的过程中永久打开句柄。这种情况绝对正常
您使用的是 NAMED 部分。关闭后它处理 - 部分 NOT DESTROYED - 因为还存在持有它的部分视图。但部分的名称已被破坏。和创建部分的新调用 - 不是打开现有的而是创建新的。
exist else one solution,这说明你都错了。
它需要 SE_CREATE_PERMANENT_PRIVILEGE,但对于演示来说这是正常的
STATIC_OBJECT_ATTRIBUTES_EX(g_oa, "\BaseNamedObjects\MyFileMappingObject", OBJ_CASE_INSENSITIVE|OBJ_PERMANENT, 0, 0);
NTSTATUS CreateAndWrite()
{
NTSTATUS status;
HANDLE hSection;
LARGE_INTEGER Size = { PAGE_SIZE };
if (0 <= (status = ZwCreateSection(&hSection, SECTION_MAP_READ|SECTION_MAP_WRITE, &g_oa, &Size, PAGE_READWRITE, SEC_COMMIT, 0)))
{
PVOID BaseAddress = 0;
SIZE_T ViewSize = 0;
if (0 <= (status = ZwMapViewOfSection(hSection, NtCurrentProcess(), &BaseAddress, 0, 0, 0, &ViewSize, ViewUnmap, 0, PAGE_READWRITE)))
{
STATIC_WSTRING(szMsg, "Message from first process.");
memcpy(BaseAddress, szMsg, sizeof(szMsg));
ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
}
ZwClose(hSection);
}
return status;
}
NTSTATUS OpenReadAndDestroy()
{
NTSTATUS status;
HANDLE hSection;
if (0 <= (status = ZwOpenSection(&hSection, SECTION_MAP_READ|DELETE, &g_oa)))
{
PVOID BaseAddress = 0;
SIZE_T ViewSize = 0;
if (0 <= (status = ZwMapViewOfSection(hSection, NtCurrentProcess(), &BaseAddress, 0, 0, 0, &ViewSize, ViewUnmap, 0, PAGE_READONLY)))
{
MessageBox(0, (PCWSTR)BaseAddress, 0, 0);
ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
}
ZwMakeTemporaryObject(hSection);
ZwClose(hSection);
}
return status;
}
if (0 <= GotPermanentPrivilege())
{
if (0 <= CreateAndWrite())
{
// at this point - no one handles for "MyFileMappingObject" exist
// we close all and even unmap view
// but 1 reference to section object exist (by OBJ_PERMANENT flag)
// and NAME is still exist
// as result we can open,map and read data :)
OpenReadAndDestroy();
}
}
你对此有何看法?
部分被破坏,所有句柄关闭后??不能使用视图? D)
p.s。
SE_CREATE_PERMANENT_PRIVILEGE - 通常只有 LocalSystem,但我们可以得到它,如果有 SE_DEBUG_PRIVILEGE+SE_IMPERSONATE_PRIVILEGE - 打开 "system" 线程并模拟它
根据您的描述,您似乎希望您的 Creator 应用关闭任何客户端的 before/irrespective。
这将有以下内容:
一旦您的创作者应用程序关闭,Windows 将自动关闭该进程打开的所有句柄,因为这是一种资源泄漏预防机制。
这意味着,如果没有客户端附加到刚刚创建的文件映射对象创建者是唯一引用它的应用程序。
当这个应用程序关闭时,所有引用文件映射对象的句柄都会关闭,这反过来会触发 Windows 规则“当文件映射对象的所有句柄都关闭时,对象将被自动释放”导致你的对象被删除!!
这就是您的客户端应用出现错误 0 的原因。
方法一:
采用这种方法的动机:
您想编写一个应用程序,并且同一应用程序的多个实例将通过共享内存进行通信。
在此类涉及共享内存进行进程间通信的应用程序中,我们开发的两个应用程序之间的唯一区别归结为“谁创建了对象”,其余操作都是使用共享内存并且是两个应用程序都一样。所以我们可以避免开发两个应用程序,而只写一个。
这种方法的另一个动机是,在客户端-服务器 LIKE 架构中,服务器必须在客户端之前启动。
如果您不想有此限制,那么此方法会有所帮助。
注意,这意味着您需要考虑更改架构,这对于现有应用程序来说可能是一项安静的任务!
实施:
// CreateFileMapping object
...
// Check the error value using GetLastError.
If (GetLastError() == ERROR_ALREADY_EXISTS)
{
// I am a client
}
else
{
// I am creator/server
}
在您的情况下,由于您允许创建者应用程序在客户端之前关闭,这意味着创建者不会像服务器那样承担太大的责任。
如果是这种情况,那么您可以使用上述方法。
或许可以让你少维护一个APP!
方法二:
让您的应用程序保持活动状态,直到至少有一个客户端连接。
但这同样会有点难以实施!
关于 UnmapViewOfFile
此功能是 close/release 系统资源的正确方法。
但这并不意味着,关闭应用程序而不调用它将使您的资源保持活力!
不调用它意味着两种情况:
- 应用程序崩溃。
- 开发者忘记调用了!
这样会造成资源泄露!
Windows 容错机制通过在进程正常或突然结束时关闭进程打开的所有句柄来缓解此问题。
所以我们可以把 UnmapViewOfFile 看成是一个函数,可以适当释放资源。
背景:我正在尝试创建一个可以被多个进程访问的内存映射文件。在下面的代码中,我只放入了与我目前必须使事情变得更简单的问题有关的代码。根据 msdn,我应该能够创建一个文件映射,映射文件的视图并关闭我从 CreateFileMapping 收到的句柄,而 MapViewOfFile 将使我的 FileMap 保持活动状态。在我 UnmapViewOfFile 之前,FileMap 应该仍然可以访问。
MSDN: CreateFileMapping function
Mapped views of a file mapping object maintain internal references to the object, and a file mapping object does not close until all references to it are released. Therefore, to fully close a file mapping object, an application must unmap all mapped views of the file mapping object by calling UnmapViewOfFile and close the file mapping object handle by calling CloseHandle. These functions can be called in any order.
问题: 在成功映射文件视图然后关闭 CreateFileMapping 接收到的句柄后,FileMap 不再存在(它应该仍然存在)并且我的 MemMapFileReader 能够创建一个错误为 0 的新地图。(当它应该收到错误 183 'already exists')
错误的解决方案:不关闭句柄允许 MemMapFileReader 程序访问它,但会导致 MemMapFileCreator 中的句柄泄漏,因为在进程关闭之前句柄永远不会关闭.
问题:我错过了什么或做错了什么?
MemMapFileCreator
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <iostream>
#define BUF_SIZE 256
TCHAR szName[] = TEXT("MyFileMappingObject");
TCHAR szMsg[] = TEXT("Message from first process.");
int _tmain()
{
HANDLE hMapFile;
LPCTSTR pBuf;
hMapFile = CreateFileMapping(
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)
szName); // name of mapping object
DWORD lastError = GetLastError();
if (hMapFile == NULL)
{
_tprintf(TEXT("Could not create file mapping object (%d).\n"),
GetLastError());
std::cin.get();
return 1;
}
pBuf = (LPTSTR)MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (pBuf == NULL)
{
_tprintf(TEXT("Could not map view of file (%d).\n"),
GetLastError());
CloseHandle(hMapFile);
std::cin.get();
return 1;
}
CopyMemory((PVOID)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR)));
CloseHandle(hMapFile);
_getch();
UnmapViewOfFile(pBuf);
return 0;
}
MemMapFileReader
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <iostream>
#pragma comment(lib, "user32.lib")
#define BUF_SIZE 256
TCHAR szName[] = TEXT("MyFileMappingObject");
int _tmain()
{
HANDLE hMapFile;
LPCTSTR pBuf;
hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE, // read/write access
0,
BUF_SIZE,
szName); // name of mapping object
DWORD lastError = GetLastError();
if (hMapFile == NULL)
{
_tprintf(TEXT("Could not open file mapping object (%d).\n"),
GetLastError());
std::cin.get();
return 1;
}
pBuf = (LPTSTR)MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (pBuf == NULL)
{
_tprintf(TEXT("Could not map view of file (%d).\n"),
GetLastError());
CloseHandle(hMapFile);
std::cin.get();
return 1;
}
MessageBox(NULL, pBuf, TEXT("Process2"), MB_OK);
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
std::cin.get();
return 0;
}
来自 MSDN:
Mapped views of a file mapping object maintain internal references to the object, and a file mapping object does not close until all references to it are released. Therefore, to fully close a file mapping object, an application must unmap all mapped views of the file mapping object by calling UnmapViewOfFile and close the file mapping object handle by calling CloseHandle. These functions can be called in any order.
CreateFileMapping 文档说要完全关闭文件,必须关闭所有句柄,顺序无关紧要。此逻辑不可逆:您不能关闭句柄并期望使用其他句柄,就好像文件映射不是 'closed'.
也就是说,清理文件映射需要按任意顺序关闭所有句柄。但是,您不能关闭底层文件映射对象并仍然使用依赖于它的视图。
CreateFileMapping()
文档说:
Mapped views of a file mapping object maintain internal references to the object, and a file mapping object does not close until all references to it are released.
CloseHandle()
文档说:
In general, CloseHandle invalidates the specified object handle, decrements the object's handle count, and performs object retention checks. After the last handle to an object is closed, the object is removed from the system.
映射视图只是将映射对象的引用计数保持在零以上,直到它们被取消映射,但它们不会保持底层 file/mapping 本身打开。
在您关闭创建者进程中的节句柄视图后不会消失,直到取消映射 bat 它的名称 - "MyFileMappingObject" 在 NT 命名空间中被销毁。结果下次调用 CreateFileMapping - 未找到命名对象 "MyFileMappingObject" 并创建新的(当您的逻辑打开现有的桅杆时)。再次 - 部分没有被破坏,但它的名字被破坏了。 你的名字 - 糟糕的解决方案 - 真的不错 - 这绝对正常 - 你不能关闭部分的句柄。这不是句柄泄漏 - 只是在您的过程中永久打开句柄。这种情况绝对正常
您使用的是 NAMED 部分。关闭后它处理 - 部分 NOT DESTROYED - 因为还存在持有它的部分视图。但部分的名称已被破坏。和创建部分的新调用 - 不是打开现有的而是创建新的。
exist else one solution,这说明你都错了。 它需要 SE_CREATE_PERMANENT_PRIVILEGE,但对于演示来说这是正常的
STATIC_OBJECT_ATTRIBUTES_EX(g_oa, "\BaseNamedObjects\MyFileMappingObject", OBJ_CASE_INSENSITIVE|OBJ_PERMANENT, 0, 0);
NTSTATUS CreateAndWrite()
{
NTSTATUS status;
HANDLE hSection;
LARGE_INTEGER Size = { PAGE_SIZE };
if (0 <= (status = ZwCreateSection(&hSection, SECTION_MAP_READ|SECTION_MAP_WRITE, &g_oa, &Size, PAGE_READWRITE, SEC_COMMIT, 0)))
{
PVOID BaseAddress = 0;
SIZE_T ViewSize = 0;
if (0 <= (status = ZwMapViewOfSection(hSection, NtCurrentProcess(), &BaseAddress, 0, 0, 0, &ViewSize, ViewUnmap, 0, PAGE_READWRITE)))
{
STATIC_WSTRING(szMsg, "Message from first process.");
memcpy(BaseAddress, szMsg, sizeof(szMsg));
ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
}
ZwClose(hSection);
}
return status;
}
NTSTATUS OpenReadAndDestroy()
{
NTSTATUS status;
HANDLE hSection;
if (0 <= (status = ZwOpenSection(&hSection, SECTION_MAP_READ|DELETE, &g_oa)))
{
PVOID BaseAddress = 0;
SIZE_T ViewSize = 0;
if (0 <= (status = ZwMapViewOfSection(hSection, NtCurrentProcess(), &BaseAddress, 0, 0, 0, &ViewSize, ViewUnmap, 0, PAGE_READONLY)))
{
MessageBox(0, (PCWSTR)BaseAddress, 0, 0);
ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
}
ZwMakeTemporaryObject(hSection);
ZwClose(hSection);
}
return status;
}
if (0 <= GotPermanentPrivilege())
{
if (0 <= CreateAndWrite())
{
// at this point - no one handles for "MyFileMappingObject" exist
// we close all and even unmap view
// but 1 reference to section object exist (by OBJ_PERMANENT flag)
// and NAME is still exist
// as result we can open,map and read data :)
OpenReadAndDestroy();
}
}
你对此有何看法? 部分被破坏,所有句柄关闭后??不能使用视图? D)
p.s。 SE_CREATE_PERMANENT_PRIVILEGE - 通常只有 LocalSystem,但我们可以得到它,如果有 SE_DEBUG_PRIVILEGE+SE_IMPERSONATE_PRIVILEGE - 打开 "system" 线程并模拟它
根据您的描述,您似乎希望您的 Creator 应用关闭任何客户端的 before/irrespective。 这将有以下内容: 一旦您的创作者应用程序关闭,Windows 将自动关闭该进程打开的所有句柄,因为这是一种资源泄漏预防机制。 这意味着,如果没有客户端附加到刚刚创建的文件映射对象创建者是唯一引用它的应用程序。 当这个应用程序关闭时,所有引用文件映射对象的句柄都会关闭,这反过来会触发 Windows 规则“当文件映射对象的所有句柄都关闭时,对象将被自动释放”导致你的对象被删除!! 这就是您的客户端应用出现错误 0 的原因。
方法一:
采用这种方法的动机: 您想编写一个应用程序,并且同一应用程序的多个实例将通过共享内存进行通信。
在此类涉及共享内存进行进程间通信的应用程序中,我们开发的两个应用程序之间的唯一区别归结为“谁创建了对象”,其余操作都是使用共享内存并且是两个应用程序都一样。所以我们可以避免开发两个应用程序,而只写一个。
这种方法的另一个动机是,在客户端-服务器 LIKE 架构中,服务器必须在客户端之前启动。 如果您不想有此限制,那么此方法会有所帮助。 注意,这意味着您需要考虑更改架构,这对于现有应用程序来说可能是一项安静的任务!
实施:
// CreateFileMapping object
...
// Check the error value using GetLastError.
If (GetLastError() == ERROR_ALREADY_EXISTS)
{
// I am a client
}
else
{
// I am creator/server
}
在您的情况下,由于您允许创建者应用程序在客户端之前关闭,这意味着创建者不会像服务器那样承担太大的责任。 如果是这种情况,那么您可以使用上述方法。 或许可以让你少维护一个APP!
方法二: 让您的应用程序保持活动状态,直到至少有一个客户端连接。 但这同样会有点难以实施!
关于 UnmapViewOfFile 此功能是 close/release 系统资源的正确方法。 但这并不意味着,关闭应用程序而不调用它将使您的资源保持活力! 不调用它意味着两种情况:
- 应用程序崩溃。
- 开发者忘记调用了!
这样会造成资源泄露! Windows 容错机制通过在进程正常或突然结束时关闭进程打开的所有句柄来缓解此问题。 所以我们可以把 UnmapViewOfFile 看成是一个函数,可以适当释放资源。