Я занимаюсь разработкой мультиплатформенного приложения 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?
}
Я довольно долго искал и не могу найти, как получить количество открытых просмотров .
Из функции CreateFileMapping :
Отображенные представления объекта сопоставления файлов поддерживают внутренние ссылки на
объект и объект сопоставления файлов не закрываются, пока все
ссылки на него опубликованы. Поэтому, чтобы полностью закрыть файл
объект сопоставления, приложение должно отобразить все сопоставленные представления файла
сопоставление объекта путем вызова UnmapViewOfFile и закрытия сопоставления файлов
дескриптор объекта, вызывая CloseHandle. Эти функции могут быть вызваны в
любой заказ.
С Функция UnmapViewOfFile :
Отмена отображения сопоставленного файла делает недействительным диапазон, занимаемый
представление в адресном пространстве процесса и делает диапазон
доступны для других распределений. Удаляет запись рабочего набора для
каждая не отображенная виртуальная страница, которая была частью рабочего набора
процесс и уменьшает размер рабочего набора процесса. Это также
уменьшает количество акций соответствующей физической страницы .
Но я не могу получить этот общий счетчик , и единственный вопрос stackoverflow по этому вопросу (который я обнаружил) остается без ответа: Количество отображенных представлений в общей памяти в Windows
Буду очень признателен, если кто-нибудь сможет мне помочь с этим.
Решение
( Примечание: Хотя может быть не на 100% надежно, см. Раздел комментариев)
Из комментариев RbMm и eryksun (Спасибо!) Я могу решить вопрос с этим кодом:
#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). Так что в конце я могу прибегнуть к решению именованной трубы (очень красиво и аккуратно).