c ++ - Adgang til antallet af delte hukommelseskortede filvisninger (Windows)

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg udvikler en C ++-applikation med flere platforme (hovedsagelig Windows og Linux), nu står jeg over for behovet for at kunne begrænse det maksimale antal tilfælde af applikationen, der kan køre samtidigt (i samme maskine).


Jeg har allerede et fælles hukommelsesmodul, der bruger:



  • Linux: System V delt hukommelse (shmget (), shmat () ...)

  • Windows: Hukommelseskortede filer (CreateFileMapping (), OpenFileMapping (), MapViewOfFile (), ...)



I Linux kan jeg nemt styre antallet af forekomster, der kører med denne slags kode:


#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
", shm.shm\_nattch);
        exit(1);
    }

    //...
    sleep(30);
}


Den nice ting ved denne kode er, at når applikationen er dræbt eller nedbrud, tager systemet omsorg for at reducere antallet af vedlagte klienter.


Nu er mit spørgsmål, hvordan man implementerer dette i Windows? (ved hjælp af hukommelseskortede filer). 'samme' -kode oversat til Windows-hukommelseskortede filer ville være (mere eller mindre):


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?
}


Jeg har søgt efter nogen tid, og jeg kan ikke finde ud af, hvordan man får antallet af åbne visninger .


Fra funktionen CreateFileMapping: [8]



  Mappede visninger af et filmappeobjekt opretholder interne referencer til
  objektet, og et filmappingsobjekt lukker ikke, før alle
  Henvisninger til det udgives. Derfor skal du fuldstændigt lukke en fil
  mapping objekt, skal en applikation omkoble alle kortlagte visninger af filen
  kortlægningsobjekt ved at ringe til UnmapViewOfFile og lukke filmappen
  objekthåndtag ved at kalde CloseHandle. Disse funktioner kan kaldes ind
  enhver ordre.



Fra funktionen UnmapViewOfFile: [9]



  Unmapping et kortfattet billede af en fil ugyldiggør det område, der besættes af
  visningen i procesens adresseområde og gør intervallet
  til rådighed for andre tildelinger. Det fjerner arbejdssætindgangen for
  hver unmapped virtuelle side, der var en del af arbejdssættet af
  behandle og reducere arbejdsindstillingsstørrelsen af ​​processen. det også
  reducerer andelstællingen for den tilsvarende fysiske side
.



Men jeg kan ikke få det delt antal , og det eneste spørgsmål om stackoverflow vedrørende dette spørgsmål (som jeg har fundet) er ubesvaret: Antal kortlagte visninger til en delt hukommelse på Windows


Jeg ville virkelig sætte pris på, om nogen kunne hjælpe mig med dette.





Løsning



( Bemærk: Selvom det måske ikke er 100\% pålideligt, se kommentarafsnittet)


Fra kommentarer fra RbMm og eryksun (Tak!) Jeg er i stand til at løse spørgsmålet med denne kode:


#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
", pobi.HandleCount);
        exit(1);
    }
    //...
    Sleep(30000);
}


Men for at være korrekt skal jeg bruge den globale kerne navneområde, som har brug for et privilegium (SeCreateGlobalPrivilege). Så i slutningen kan jeg ty til den navngivne rørløsning (meget flot og pænt).

Bedste reference


Som nævnt eryksun, mest pålidelige måde at gøre dette - brug CreateNamedPipe funktion. vi kan bruge nMaxInstances parameteren - Det maksimale antal instanser, der kan oprettes til dette rør. på næste måde. alle forekomster af applikationen på startforsøg opretter pipetilfælde. hvis dette er ok - vi kan løbe, ellers er grænsen nået. [13]


kode kan være næste:


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;
}


og brug, siger for N tilfælde


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