c++ - 访问共享内存映射文件 View 的数量 (Windows)

标签 c++ c windows memory-mapped-files

我正在开发一个多平台 C++ 应用程序(主要是 Windows 和 Linux),现在我面临能够限制可能同时运行的应用程序实例的最大数量(在同一台机器上)的需求.

我已经有一个共享内存模块使用:

  • Linux:System V 共享内存(shmget()、shmat()...)
  • Windows:内存映射文件(CreateFileMapping()、OpenFileMapping()、MapViewOfFile() 等)

在 linux 中,我可以很容易地控制使用这种代码运行的实例数:

#include <stdio.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdlib.h>

int main(void)
{
    struct shmid_ds shm;
    int shmId;
    key_t shmKey = 123456; // A unique key...

    // Allocating 1 byte shared memory segment
    // open it if already existent and rw user permission
    shmId = shmget(shmKey, 1, IPC_CREAT|0x0180);

    // Attach to the shared memory segment
    shmat(shmId, (char *) 0, SHM_RDONLY);

    // Get the number of attached "clients"
    shmctl(shmId, IPC_STAT, &shm);

    // Check limit
    if (shm.shm_nattch > 4) {
        printf("Limit exceeded: %ld > 4\n", shm.shm_nattch);
        exit(1);
    }

    //...
    sleep(30);
}

这段代码的好处是,当应用程序被终止或崩溃时,系统会小心减少连接的客户端数量。

现在我的问题是,如何在 Windows 中实现它?(使用内存映射文件)。翻译成 Windows 内存映射文件的“相同”代码将(或多或少):

void functionName(void)
{
    // Create the memory mapped file (in system pagefile)
    HANDLE hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE|SEC_COMMIT,
                  0, 1, "Global\\UniqueShareName");

    // Map the previous memory mapped file into the address space
    char *addr = (char*)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);

    // How can I check now the number of views mapped?
}

我已经搜索了很长时间,但找不到如何获取打开的 View 数

来自 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.

来自 UnmapViewOfFile function :

Unmapping a mapped view of a file invalidates the range occupied by the view in the address space of the process and makes the range available for other allocations. It removes the working set entry for each unmapped virtual page that was part of the working set of the process and reduces the working set size of the process. It also decrements the share count of the corresponding physical page.

但我无法获得那个共享计数,并且关于此事(我发现)的唯一 stackoverflow 问题没有答案:Number of mapped views to a shared memory on Windows

如果有人能帮助我,我将不胜感激。


解决方案

(注意:虽然可能不是 100% 可靠,但请参阅评论部分)

根据 RbMmeryksun 的评论(谢谢!)我可以用这段代码解决问题:

#include <stdio.h>
#include <windows.h>
#include <winternl.h>

typedef NTSTATUS (__stdcall *NtQueryObjectFuncPointer) (
            HANDLE                   Handle,
            OBJECT_INFORMATION_CLASS ObjectInformationClass,
            PVOID                    ObjectInformation,
            ULONG                    ObjectInformationLength,
            PULONG                   ReturnLength);

int main(void)
{
    _PUBLIC_OBJECT_BASIC_INFORMATION pobi;
    ULONG rLen;

    // Create the memory mapped file (in system pagefile) (better in global namespace
    // but needs SeCreateGlobalPrivilege privilege)
    HANDLE hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE|SEC_COMMIT,
                  0, 1, "Local\\UniqueShareName");

    // Get the NtQUeryObject function pointer and then the handle basic information
    NtQueryObjectFuncPointer _NtQueryObject = (NtQueryObjectFuncPointer)GetProcAddress(
            GetModuleHandle("ntdll.dll"), "NtQueryObject");

    _NtQueryObject(hMap, ObjectBasicInformation, (PVOID)&pobi, (ULONG)sizeof(pobi), &rLen);

    // Check limit
    if (pobi.HandleCount > 4) {
        printf("Limit exceeded: %ld > 4\n", pobi.HandleCount);
        exit(1);
    }
    //...
    Sleep(30000);
}

但为了正确起见,我应该使用全局内核命名空间,它需要一个特权 (SeCreateGlobalPrivilege)。所以最后我可能会求助于命名管道解决方案(非常好和整洁)。

最佳答案

如 eryksun 所述,最可靠的方法是使用 CreateNamedPipe功能。我们可以使用 nMaxInstances 参数 - 可以为此管道创建的最大实例数。 在接下来的方法中。启动时应用程序的每个实例都尝试创建管道实例。如果没问题 - 我们可以运行,否则达到限制。

代码可以是下一个:

BOOL IsLimitReached(ULONG MaxCount)
{
    SECURITY_DESCRIPTOR sd;
    InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);

    SECURITY_ATTRIBUTES sa = { sizeof(sa), &sd, FALSE };

    HANDLE hPipe = CreateNamedPipe(L"\\\\.\\pipe\\<some pipe>", 
        PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_READMODE_BYTE, MaxCount, 0, 0, 0, &sa);

    if (hPipe == INVALID_HANDLE_VALUE)
    {
        ULONG dwError = GetLastError();

        if (dwError != ERROR_PIPE_BUSY)
        {
            // handle error 
        }
        return TRUE;
    }

    return FALSE;
}

并使用,比如 N 个实例

    if (!IsLimitReached(N))
    {
        MessageBoxW(0, L"running..",0,0);
    }

关于c++ - 访问共享内存映射文件 View 的数量 (Windows),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50108286/

相关文章:

c++ - cvGet2D 替代方案

c++ - 静态本地与本地。 C 和 C++ 哪个更快?

C代码将文件移动到垃圾箱

c - 如何事先确定无符号计算是否可能溢出?

java - IntelliJ错误打开zip文件或JAR list 丢失

c - 在除 Windows 之外的所有内容上添加构建标志

c++ - Visual C++ DLL 项目中的冲突运行时库

c++ - 是否可以在指定时间后绕过 std::cin ?

c - 冗余 __thread 和 omp threadlocal 声明

c++ - 当鼠标悬停在某个控件上时,如何设置自定义光标?