Чтение онлайн

ЖАНРЫ

Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform

Кёртен Роб

Шрифт:

4. Теперь клиент посылает запрос непосредственно нужному серверу. Маршрут следования сообщения здесь идентичен описанному в предыдущем пункте, за исключением того, что на этот раз связь с сервером осуществляется напрямую, а не через администратор процессов.

После того как пройдены этапы 1 и 3, все дальнейшие коммуникации осуществляются аналогично этапу 4. В вышеприведенном примере все сообщения типа open, read и close идут по маршруту, описанном в этапе 4. Заметьте, что вся последовательность рассмотренных событий была запущена вызовом open, но само сообщение open все равно дошло до сервера-адресата так, как это описано этапом 4.

Для
особо любопытных: на самом деле я пропустил в изложении один этап. На этапе 2, когда клиент спрашивает
qnet
об узле
wintermute
,
qnet
должен сначала выяснить, кто такой wintermute. Это может привести к еще одной сетевой транзакции для разрешения имени узла. Приведенный выше рисунок корректен, если предположить, что
qnet
заранее знал про узел с именем
wintermute
.

Мы еще вернемся к сообщениям, используемым функциями open, read и close (а также другими функциями) в главе «Администраторы ресурсов».

Особенности обмена сообщениями в сети

Итак, как только соединение установлено, все дальнейшие операции обмена сообщениями осуществляются в соответствии с этапом 4, как указано на рисунке. Это может привести вас к ошибочному представлению, что передача сообщений по сети идентична локальной. К сожалению, это не так. Вот список отличий:

• более длительные задержки;

• функция ConnectAttach возвращает признак успешного соединения независимо от того, является ли узел доступным или нет — реальный признак ошибки проявляется только при первой попытке передать сообщение;

• функция MsgDeliverEvent не обеспечивает достаточной надежности;

• функции MsgReply, MsgRead, MsgWrite являются блокирующими вызовами (в локальном варианте они не являлись таковыми);

• функция MsgReceive не будет принимать все данные, посланные клиентом; сервер будет должен вызывать функцию MsgRead для получения окончательных остальных данных.

Более длительные времена задержки

Поскольку передача сообщений теперь выполняется в некоторой среде, а не прямым копированием «память-память» под управлением ядра, можно смело ожидать, что затраты времени на передачу сообщения будут существенно выше (100-Мбитный Ethernet в сравнении с 128-битным динамическим ОЗУ с тактированием 100 МГц будет ориентировочно на один или два порядка медленнее). В дополнение к этому также будут сказываться накладные расходы протокола и повторные попытки передачи в сбойных сетях.

Воздействие на функцию ConnectAttach

Когда вы вызываете функцию ConnectAttach, вы задаете ей идентификаторы ND, PID и CHID. Все, что при этом происходит в QNX/Neutrino, заключается в возврате ядром идентификатора соединения драйверному потоку

qnet
, изображенному выше на рисунке. Поскольку никакого сообщения еще не отправлено, вы не имеете информации о том, доступен ли узел, к которому вы только что подсоединились, или нет. В обычном случае это не проблема, потому что большинство клиентов не будет самостоятельно вызывать ConnectAttach и скорее воспользуется библиотечной функцией open, которая перед передачей сообщения «open» сама вызывает ConnectAttach. Это практически немедленно дает информацию о доступности удаленного узла.

Воздействие на функцию MsgDeliverEvent

Когда сервер вызывает функцию MsgDeliverEvent

локально, ответственность за доставку события целевому потоку ложится на ядро. В сетевом варианте сервер также может вызывать функцию MsgDeliverEvent, но на этот раз ядро доставит «заготовку» этого события администратору
qnet
, возлагая на него ответственность за доставку этой «заготовки» удаленному
qnet
, который уже доставит реальное событие клиенту. Так вот, на стороне сервера с этим вызовом могут возникнуть проблемы, потому что он не является блокирующим. Это означает, после вызова MsgDeliverEvent сервер продолжает выполняться, и поздно уже оглядываться и говорить «Знаете, очень не хочется вас огорчать, но помните тот вызов MsgDeliverEvent? Так вот, он не сработал...»

Воздействие на функции MsgReply, MsgRead и MsgWrite

Чтобы уберечь функции MsgReply, MsgRead и MsgWrite от вышеупомянутой проблемы MsgDeliverEvent, эти функции при использовании их в сети преобразуются в блокирующие вызовы. В локальном случае они бы просто передали данные и разблокировались; в сети же мы должны либо удостовериться, что данные были доставлены клиенту (в случае MsgReply), либо собственно передать данные по сети клиенту или от него (в случае двух других функций).

Воздействие на функцию MsgReceive

Функция MsgReceive (при использовании в сети) тоже оказывается под влиянием. На момент разблокирования функции MsgReceive на стороне сервера

qnet
может еще не успеть передать все данные клиента. Это делается из соображений производительности.

В структуре

struct _msg_infо
, передаваемой функции MsgReceive в качестве последнего параметра (мы подробно рассматривали эту структуру в параграфе «Кто послал сообщение?»), есть два флага:

msglen Указывает на фактическое количество данных, переданное функцией MsgReceive (
qnet
любит передавать по 8Кб за один раз).
srcmsglen Указывает на количество данных, которое клиент хотел передать (определяется клиентом).

Таким образом, если бы клиент желал передать 1 мегабайт данных по сети, MsgReceive сервера разблокировалась бы, установив параметр msglen в значение 8192 (указывая, что 8192 байта доступны в буфере); параметр srcmsglen при этом равнялся бы 1048576 (указывая, что клиент пытался переслать 1 мегабайт данных).

Затем сервер использует MsgRead для получения остальной части данных из адресного пространства клиента.

Несколько замечаний о дескрипторах узлов

Еще одна любопытная вещь, которой мы еще не касались в обсуждениях обмена сообщениями, — это дескрипторы узлов, для краткости обозначаемые «ND» (сокр. от Node Descriptor — прим. ред.).

Вспомните: в наших примерах мы использовали символьные имена узлов, например,

/net/wintermute
. В QNX4 (предыдущая версия QNX до появления QNX/Neutrino) вся работа в сети была основана на концепции идентификатора узла, небольшого целого числа, уникально определяющего узел сети. Таким образом, в терминах QNX4 мы говорили бы что-то вроде «узел 61», или «узел 1», и это отражалось бы и на вызовах функций тоже.

Поделиться с друзьями: