Шины сообщений (магистрали). Черновой вариант API |
Несколько упрощая, всю работу IPC можно описать с помощью такого API:
int RegisterMessageBus(const char *bus_name); void UnregisterMessageBus(int bus_id); int EnumMessageBus(char *buffer, int buffer_size); int RegisterMessageFilter(int bus_id, int *msg_types, int count); void* AllocMessage(int size); void FreeMessage(void *ptr_msg); void SendMessage(int bus_id, int type, void *ptr_msg); void* GetMessage(int &msg_type);
Далее приводится упрощенное описание реализации вышеприведенных функций. Описание в двух вариантах: со стороны пользовательского процесса (USER) и со стороны ядра (KERNEL).
Первые три функции предназначены для управления самими шинами: создание, удаление, получение информации о шинах.
int RegisterMessageBus(const char *bus_name);
Функция регистрирует поток на магистрали bus_name. bus_name - наименование магистрали. Функция возвращает идентификатор, который фигурирует во всех остальных обращениях к магистрали.
Если зарегистрированный поток по каким-то причинам не дерегистрировался перед завершением (core dumped или просто программист забыл), ядро выполняет освобождение ресурсов. В качестве идентификатора ядро может возвращать указатель на свой объект "магистраль" (из соображений скорости доступа), но это может привести к большой нестабильности ядра. Лучше возвращать искусственный id, который ссылается на объект ядра "магистраль"
Это внутренняя функция ядра. При любом, нормальном или аварийном завершении потока, ядро освобождает все занятые потоком сообщения, проводит дерегистрацию во всех магистралях. Если поток был единственным зарегистрированным на магистрали, то удаляется и магистраль.
void UnregisterMessageBus(int bus_id);
Поток снимается с регистрации на магистрали bus_id. Поток больше не будет получать сообщения с этой шины.
Ядро находит магистраль bus_id и если поток зарегистрирован в этой магистрали удаляет его фильтр сообщений. Если поток был единственным зарегистрированным на магистрали, то удаляется и магистраль. При этом сообщения, полученные с этой магистрали и находящиеся в очереди потока не удаляются.
int EnumMessageBus(char *buffer, int buffer_size);
Функция формирует блок памяти (в buffer) такого формата:
"bus_name"\0"bus_name"\0..."bus_name"\0\0 buffer_size - размер буфера
Если размер буфера достаточен для хранения всего списка магистралей, функция возвращает 0. В противном случае возвращается необходимый размер буфера.
Следующие две функции управляют распределением памяти сообщений.
void* AllocMessage(int size);
Функция возвращает указатель на начало сообщения размером size в адресном пространстве потока. Если не удалось создать сообщение, возвращается 0.
void FreeMessage(void *ptr_msg);
Выделенное ранее сообщение удаляется.
Далее 3 функции управляющие приемом/передачей сообщений.
int RegisterMessageFilter(int bus_id, int *msg_types, int count);
Функция устанавливает на шине bus_id фильтр сообщений для текущего потока. Потоку будут доставляться сообщения, типы которых указаны в фильтре.
msg_types - массив типов сообщений count - количество сообщений в фильтре
В случае удачного завершения возвращается 0. В противном случае (например, если поток не зарегистрирован на магистрали) возвращается -1.
void SendMessage(int bus_id, int type, void *ptr_msg);
Функция отправляет сообщение ptr_msg типа type на магистраль bus_id.
Проверяется:
Если хотя бы одно из этих условий не выполняется, отправка сообщения не производится (может имеет смысл завершить поток аварийно).
Атрибуты региона содержащего сообщение заменяются на RO. Это необходимо по двум причинам:
В принципе, можно ввести функцию повторного "быстрого" выделения сообщения, когда атрибуты снова меняются на RW, единственным условием является наличие только одного владельца сообщения.
Ядро проходит по потокам, зарегистрированным на магистрали и сравнивает тип передаваемого сообщения (type) с их фильтрами. Если сообщение можно передать потоку-получателю, выполняются такие действия:
void* GetMessage(int &msg_type);
Возвращает указатель на первое выбранное из очереди сообщение. Тип сообщения сохраняется в msg_type. Если очередь сообщений пуста, поток будет приостановлен.
Ядро пытается выбрать первое сообщение из очереди потока. Если очередь пуста, поток выносится из списка потоков планировщика.