
- •Определение идентификаторов узла, процесса и канала (nd/pid/chid) нужного сервера.
- •Обмен длинными сообщениями
- •Обмен составными сообщениями
- •Сообщения типа «импульс» (pulse)
- •Функция MsgDeliverEvent()
- •Флаги канала
- •Обмен сообщениями в сети
- •Преимущества очередей сообщений стандарта posix
- •Функция Описание
- •Именованные каналы
Флаги канала
_NTO_CHF_FIXED_PRIORITY
Принимающий поток не изменит приоритет в зависимости от приоритета отправителя. Если этот флаг не установлен, то приоритет принимающего сообщение потока изменяется на приоритет потока-отправителя.
_NTO_CHF_UNBLOCK
Ядро посылает импульс всякий раз, когда поток клиента пытается разблокироваться. Чтобы клиент мог разблокироваться, сервер должен ему ответить.
_NTO_CHF_THREAD_DEATH
Ядро посылает импульс всякий раз, когда блокированный на этом канале поток «умирает». Это полезно для серверов, которые желают поддержать фиксированную популяцию потоков в пуле, т. е. количество потоков, доступных для обслуживания запросов.
_NTO_CHF_DISCONNECT
Ядро посылает импульс всякий раз после того, как уничтожается последнее из имевшихся соединений сервера с некоторым клиентом.
_NTO_CHF_SENDER_LEN
Ядро доставляет серверу, наряду с остальной информацией, размер клиентского сообщения.
Флаг _NTO_CHF_UNBLOCK
Этот флаг имеет несколько особенностей при его применении, интересных и для клиента, и для сервера.
Если сервер не устанавливает флаг _NTO_CHF_UNBLOCK, то клиент может разблокироваться от MsgSend() (или MsgSendv(), MsgSendvs() или другой функции этого семейства) когда захочет (например, по приему сигнала или по тайм-ауту ядра).
Если сервер устанавливает флаг _NTO_CHF_UNBLOCK, то клиент не может разблокироваться без посылки сервером сообщения по MsgReply() и, следовательно, может оказаться в состоянии dead line.
При однопоточном сервере происходит следующее:
Состояние клиента Состояние сервера
----------------------------------------------------------------------------------------------------
Клиент посылает запрос серверу Блокирован Обработка
Клиент получает сигнал Блокирован Обработка
Клиент передает импульс серверу Блокирован Обработка (первого сообщения)
Сервер завершает обработку Разблокирован, получены Обработка
первого запроса и отвечает клиенту корректные данные (импульса)
Это не помогает клиенту разблокироваться, но зато обеспечивает серверу корректное завершение работы.
Если важно, чтобы сервер среагировал каким-то действием на посланный ядром импульс, то существует два способа реализации этого:
• Создать еще один поток в сервере, который «слушал» бы канал на предмет импульсов от ядра. Этот второй поток будет отвечать за отмену операции, выполняемой первым потоком. Отвечать клиенту может любой из этих двух потоков.
• Не выполнять задание клиента в потоке непосредственно, а поставить его в очередь заданий.
Какой из методов выбирать зависит от типа работы, которую выполняет сервер.
В первом случае сервер активно выполняет работу для клиента, так что нет иного выбора, как применить второй поток, который слушал бы импульсы от ядра, сообщающие о разблокировании (импульсы разблокирования).
Во втором случае работу делает не сам сервер, а кто-то другой — возможно, оборудование, которому приказано «сходи и набери данных». При таком варианте поток сервера будет в любом случае блокирован по функции MsgReceive(), ожидая от оборудования признака завершения операции.
В обоих случаях сервер обязан ответить клиенту, иначе клиент останется заблокированным.
При многопоточном сервере происходит следующее:
Здесь мы имеем гонки потоков — первый поток на момент получения вторым импульса мог быть уже почти готов ответить клиенту. Если ответит второй поток (тот, который получил импульс), то есть шанс, что клиент разблокируется и передаст серверу еще одно сообщение. При этом первый поток сервера получает шанс завершить работу по первому запросу и ответить полученными данными на второй запрос:
Также возможен такой вариант: поток, получивший импульс, готовится ответить клиенту, а в это время отвечает первый поток. Получается то же самое, что и раньше — первый поток разблокирует клиента, клиент передает второй запрос, второй поток (тот, который получил импульс) разблокирует клиента по второму запросу.
Здесь мы имеем ситуацию с двумя параллельными потоками обработки (один вызван сообщением клиента и один — импульсом). Обычно в таких ситуациях применяются мутексы.
К сожалению, это привело бы к проблеме — мутекс нужно было бы захватить немедленно после вызова MsgReceive() и освободить перед вызовом MsgReply(). Это, конечно, будет работать, но это войдет в противоречие с самим предназначением импульса разблокирования! (Сервер мог бы либо получить сообщение и игнорировать импульс разблокирования, пока не ответит клиенту, либо получить импульс разблокирования и отменить второй запрос клиента.)
Единственный способ корректно обойти проблему состоит в том, чтобы переложить работу по синхронизации потоков на ядро. В качестве средства синхронизации используется флаг _NTO_MI_UNBLOCK_REQ.
Для этого отведен один бит в информационной структуре сообщения (это структура типа struct _msg_infо, которая передается функции MsgReceive() в качестве последнего параметра, и которую можно также впоследствии получить по идентификатору отправителя, вызвав функцию MsgInfo()).