
Использование класса boost::shared_mutex
Зачастую простой мьютекс, защищающий память, неоптимален. Например, в онлайн-игре список игровых комнат изменяется нечасто — когда кто-то из игроков решает открыть новую комнату, например, раз в несколько секунд. Считывается же он за одну секунду десятки раз, и выстраивать клиентов в очередь для этого не имеет смысла.
Подобные механизмы (так называемая блокировка чтения-записи) существуют во многих языках программирования и библиотеках. Например.
Embarcadero Delphi: IMultiReadExclusiveWrite.
POSIX: pthread_rwlock_t.
Java: ReadWriteLock, ReentrantReadWriteLock.
.NET Framework: System.Threading.ReaderWriterLockSlim.
Boost: boost::shared_mutex.
Пример (читатель) boost::shared_mutex g_Mutex; void reader_proc() { // . . . g_Mutex.lock_shared(); // Чтение g_Mutex.unlock_shared(); // . . . } |
Пример (писатель) void writer_proc() { // . . . g_Mutex.lock(); // Запись g_Mutex.unlock(); // . . . } |
Межпроцессное взаимодействие
Объекты синхронизации
Используются для работы с разл. процессами.
объекты синхронизации, используемые для взаимодействия между процессами
- События (Windows API)
- Условные переменные (POSIX) (не явл. объектом ядра, поэтому нельзя использовать для разделения между процессами)
- Мьютексы
- Семафоры
События Windows API
HANDLE WINAPI CreateEvent (
__in_opt LPSECURITY_ATTRIBUTES lpEventAttributes,
__in BOOL bManualReset,
__in BOOL bInitialState,
__in_opt LPCTSTR lpctszName //не обязательно указатель на строку, опр-ет имя события );
HANDLE WINAPI OpenEvent (
__in DWORD dwDesiredAccess,
__in BOOL bInheritHandle,
__in LPCTSTR lpctszName // -- - -- );
Мьютексы Windows API
HANDLE WINAPI CreateMutex (
__in_opt LPSECURITY_ATTRIBUTES lpMutexAttributes, //Атрибуты безопасности
__in BOOL bInitialOwner, //Флаг начального владельца – свободен или занят
__in_opt LPCTSTR lpctszName //Имя );
HANDLE WINAPI OpenMutex (
__in DWORD dwDesiredAccess, //Константа, опр-ет, что хотим сделать (чтобы использовать мьютекс, необх. только синхронизировать права доступа
__in BOOL bInheritHandle, //Определяет, можно ли дескриптор наследовать в дочерних процессах
__in LPCTSTR lpctszName );
Семафоры Windows API
HANDLE WINAPI CreateSemaphore (
__in_opt LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
__in LONG lInitialCount, //Начальное
__in LONG lMaximumCount, //Максимальное значение счетчика
__in_opt LPCTSTR lpctszName );
HANDLE WINAPI OpenSemaphore (
__in DWORD dwDesiredAccess,
__in BOOL bInheritHandle,
__in LPCTSTR lpctszName );
Использование разделяемых событий в Windows API
Пример (event.h)
#ifndef EVENT_H__
#define EVENT_H__
#define MY_IPC_EVENT_NAME \
"event_CB44CFBF 52C3 487A 95A0 1233F5A4393C"
#define MY_IPC_MUTEX_NAME \
"mutex_0DF33C8C 71FF 4358 B10A AD7F0B7484F7"
#endif // EVENT_H__
//GUID (глобальный уникальный идентификатор)
Пример (main_parent.cpp)
#include "event.h"
// . . .
int main()
{ HANDLE hEvent = CreateEvent(
NULL, // lpEventAttributes
FALSE, // bManualReset
FALSE, // bInitialState
_T(MY_IPC_EVENT_NAME)); // lpctszName
// . . .
}
Строковая константа определена в заголовочном файле
Клиент гарантировано открылся после сервера
Пример (main_child.cpp)
#include "event.h"
// . . .
int main()
{ HANDLE hEvent = OpenEvent(
SYNCHRONIZE, // dwDesiredAccess КОНСТАНТА
FALSE, // bInheritHandle
_T(MY_IPC_EVENT_NAME)); // lpctszName //макрос, если объявлен юникод
// . . .
}
работает как с utf-16 как с юникодом, define _T(s) добавляет букву L (L##S), т.е. находится в более широкой кодировке)
функции принимают все строки в формате unicode
Способы доступа к объектам ядра в других процессах
Средства межпроцессного обмена дескрипторами
- Именованные объекты;
- Наследование дескрипторов дочерними процессами;
- Дублирование дескрипторов (DuplicateHandle()).
Семафоры POSIX
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
int sem_init (
sem_t *pSem,
int nPShared, //Равен 0, если хотим сделать локальным для одного процесса, не 0 – разделять между разными (fork()ed)
unsigned int uValue);
sem_t *sem_open(
const char *pcszName, int nOFlag,
/* unsigned long ulMode, unsigned int uValue */ ...);
возможные значения флагов параметров функции sem_open()
nOFlag
O_CREAT – задает операцию создания. Если сброшен – только пытается найти ?. Передается две доп. переменные.
O_EXCL – эксклюзивный. Пытается создать. Если уже существует, то возвр. NULL в качестве указателя. Если флаг не указан, в комбинации с O_CREAT возвращает ук-ль на начало
ulMode
S_IRWXU S_IRUSR S_IWUSR S_IXUSR
S_IRWXG S_IRGRP S_IWGRP S_IXGRP
S_IRWXO S_IROTH S_IWOTH S_IXOTH
Мюьтексы POSIX
int pthread_mutex_init (
pthread_mutex_t *pMutex,
const pthread_mutexattr_t *pcAttr); //Указатель на аттрибуты
значения параметра функции pthread_mutexattr_setpshared()
nPShared
PTHREAD_PROCESS_PRIVATE
PTHREAD_PROCESS_SHARED
int pthread_mutexattr_init( pthread_mutexattr_t *pAttr);
int pthread_mutexattr_destroy(pthread_mutexattr_t *pAttr);
#ifdef _POSIX_THREAD_PROCESS_SHARED
int pthread_mutexattr_setpshared(pthread_mutexattr_t *pAttr,int nPShared); #endif
Разделяемая память POSIX
Сущ-ют средства, которые позволяют процессам обмениваться данными. Запрашиваем память, подключаемся в адресное пространство процесса, начиная с нек. адреса. Мб видна другим процессам.
POSIX shmget()
#include <sys/types.h>
#include <sys/shm.h>
int shmget(key_t nKey, //Ключ, опред. ID, номер участка
int nSize, int nShmFlg);
Предназначениа для создания дескриптора области памяти. Параметры аналогичны:
возможные значения флагов параметров функции shmget()
nKey IPC_PRIVATE
nShmFlg IPC_CREAT IPC_EXCL
младшие 9 бит – флаги, которые задают права доступа на разделяемую память
Создает объект и возвращает дескриптор
#include <sys/types.h>
#include <sys/shm.h>
void *shmat (//-at – attach, подключение данной области текущей памяти
int nShmId, //дескриптор области, то, что возвр. пред. функция
const void *pvShmAddr, //адрес в адресном пространстве процессов, NULL – ядро ОС самостоятельно находит адрес, чтобы подключить зад. обл. разделяемой памяти
int nShmFlg); //Флаг – поределяет способ
int shmdt(const void *pvShmAddr);
возможные значения флагов параметров функции shmat()
nShmFlg
SHM_RND (SHMLBA) – если указ-м конкретный адрес, то адрес будет выровнен
SHM_RDONLY – процесс только считывает, записывать не может
Пример
const key_t g_cKey = 1917;
// . . .
int nShmId = shmget(g_cKey,
sizeof (struct connect), //размер раздела памяти
IPC_CREAT | 0644); //флаг, пытается создать область памяти
if (nShmId < 0) //-1 если нет
{
perror("shmget"); //печатает такое сообщение в консоли
exit(1);
}
struct connect *pConnect = (struct connect *) shmat(nShmId, NULL, 0); //явное преобразование указателя // . . .
shmdt(pConnect); //отсоединяет от адр. пространства текущего процесса.
Проблема – ключ-идентификатор трудно выбрать
При помощи fork можно задавать параметры командной строки, перед. дочернему процессу.
Решение проблемы дублирования ключей
Варианты
- Использование в качестве ключа константы IPC_PRIVATE.
- Генерирование ключа при помощи функции ftok().
POSIX ftok()
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pcszPathName, int nProjId);
По заданному файлу генерируем ключ. Пар-ры – (путь, номер проекта?)
stat() – функция для получения различных параметров. Вернет один ключ, даже если запускался в разл. процессах.
Разделяемая память Windows API
ANDLE WINAPI CreateFileMapping( //Создание объекта для работы с разд. памятью
__in HANDLE hFile,
__in_opt LPSECURITY_ATTRIBUTES lpAttributes, //NULL
__in DWORD dwProtect, //Способ доступа в памяти
__in DWORD dwMaximumSizeHigh,
__in DWORD dwMaximumSizeLow,
__in_opt LPCTSTR lpctszName );
возможные значения флагов параметров функции
dwProtect
PAGE_READONLY – исп. код из памяти и читать
PAGE_READWRITE
PAGE_WRITECOPY – режим копирования при записи
PAGE_EXECUTE_READ . . .
LPVOID WINAPI MapViewOfFile( //подсоединяет к адр. пространству процесса и возвр. указатель на ней
__in HANDLE hFileMappingObject,
__in DWORD dwDesiredAccess,
__in DWORD dwFileOffsetHigh, //смещение внутри обл. памяти, NULL – общая память
__in DWORD dwFileOffsetLow,
__in SIZE_T dwNumberOfBytesToMap //Размер );
BOOL WINAPI UnmapViewOfFile( //Передает адр. пр-во, отсоединяет.
__in LPCVOID lpcvBaseAddress );
dwDesiredAccess
FILE_MAP_READ
FILE_MAP_WRITE (и чтение тоже)
FILE_MAP_COPY
Каналы POSIX
нек. обл. памяти, в режиме очереди FIFO
POSIX pipe()
#include <unistd.h> // <io.h>
int pipe(int anFD[2]); // anFD[0] чтение, anFD[1] запись – указатель на массив из 2х дескрипторов
int dup2(int nOldFD, int nNewFD); //Дескриптор файла, новое значение. Закрывает файл, если нужно. Открывает 1, делает равным 1
ssize_t read(int nFD, void *pvBuf, size_t uCount); //то же, что и size_t но со значением nCount, если нет проблем
ssize_t write(int nFD, const void *pcvBuf, size_t uCount);
int eof(int nFD);
int close(int nFD); //Закрытие файла
Использование каналов в POSIX
Пример
$ ls j | more
Выводит содержимое текущего каталога, по умолчанию – список файлов | Считывает файл и выводит его постранично, если слишком большой
Реализация bash
1 pipe(anFD)
2 fork() (2 раза)
3 close(anFD[i]) (2 раза)
1 Создает канал для обмена, передает массив, создает директории 2 Создание процесса в POSIX В дочернем 0, в род. ID
Реализация ls
1 dup2(anFD[1], 1) //1 – вывод на консоль
2 close(anFD[i]) (2 раза)
3 execve("ls", argv, envp)
Тот же заполненный массив из двух дескрипторов, созд. новый дескриптор. То, что выводим на консоль, попадет в канал, 2й дескриптор продолжит существовать.
Реализация more
1 dup2(anFD[0], 0)
2 close(anFD[i]) (2 раза)
3 execve("more", argv, envp)
попадает в канал и читается другим процессом