Маршрутизация, буферизация и регистрация сообщений
В распределенных системах не существует памяти, непосредственно доступной процессам, работающим на разных компьютерах, поэтому взаимодействие процессов может осуществляться только путем передачи сообщений через сеть. На основе механизма передачи сообщений работают все сетевые службы, предоставляющие пользователям сети разнообразные услуга - доступ к удаленным файлам, принтерам, почтовым ящикам и т.п. В сообщениях переносятся запросы от клиентов некоторой службы к соответствующим серверам — например, запрос на просмотр содержимого определенного каталога файловой системы, расположенной на сетевом сервере. Сервер возвращает ответ — набор имен файлов и подкаталогов, входящих в данный каталог, также помещая его в сообщение и отправляя его по сети клиенту.
Сообщение — это блок информации, отформатированный процессом-отправителем таким образом, чтобы он был понятен процессу-получателю. Сообщение состоит из заголовка, обычно фиксированной длины, и набора данных определенного типа переменной длины. В заголовке, как правило, содержатся следующие элементы:
Адрес — набор символов, уникально определяющих отправляющий и получающий процессы. Адресное поле, таким образом, состоит из двух частей — адреса процесса-отправителя и адреса процесса-получателя. Адрес каждого процесса может, в свою очередь, иметь некоторую структуру, позволяющую найти нужный процесс в сети, состоящей из большого количества компьютеров.
Последовательный номер, являющийся идентификатором сообщения. Используется для идентификации потерянных сообщений и дубликатов сообщений в случае отказов в сети.
Структурированная информация, состоящая в общем случае из нескольких частей: поля типа данных, поля длины данных и поля значения данных. Поле типа данных определяет, например, что данные являются целым числом или же представляют собой строку символов. Второе поле определяет длину передаваемых в сообщении данных (обычно в байтах), то есть размер следующего поля сообщения. Сообщение может включать несколько элементов, состоящих из описанных трех полей. В тех случаях, когда сообщение всегда переносит данные одного и того же типа, поле типа может быть опущено. То же касается поля длины данных — для тех типов сообщений, которые переносят данные фиксированного формата, но такая ситуация характерна только для протоколов низкого уровня.
В любой сетевой ОС имеется подсистема передачи сообщений, называемая также транспортной подсистемой, которая обеспечивает набор средств для организации взаимодействия процессов по сети. Назначение этой системы — экранировать детали сложных сетевых протоколов от программиста. Подсистема позволяет процессам взаимодействовать посредством достаточно простых примитивов. В самом простом случае системные средства обеспечения связи могут быть сведены к двум основным коммуникационным примитивам, один send (отправить) — для посылки сообщения, другой receive (получить) — для получения сообщения. В дальнейшем на их базе могут быть построены более мощные средства сетевых коммуникаций, такие как распределенная файловая система или служба вызова удаленных процедур, которые, в свою очередь, также могут служить основой для работы других сетевых служб.
Транспортная подсистема сетевой ОС имеет обычно сложную структуру, отражающую структуру семиуровневой модели взаимодействия открытых систем. Представление сложной задачи сетевого взаимодействия компьютеров в виде иерархии нескольких частных задач позволяет организовать это взаимодействие максимально гибким образом. В то же время каждый уровень модели OSI экранирует особенности лежащих под ним уровней от вышележащих уровней, что делает средства взаимодействия компьютеров все более универсальными по мере продвижения вверх по уровням. Таким образом, в процесс выполнения примитивов send и receive вовлекаются средства всех нижележащих коммуникационных протоколов.
Несмотря на концептуальную простоту примитивов send и receive, существуют различные варианты их реализации, от правильного выбора которых зависит эффективность работы сети. В частности, эффективность зависит от способа задания адреса получателя. Не менее важны при реализации примитивов передачи сообщений ответы и на другие вопросы. В сети всегда имеется один получатель или их может быть несколько? Требуется ли гарантированная доставка сообщений? Должен ли отправитель дождаться ответа на свое сообщение, прежде чем продолжать свою работу? Как отправитель, получатель и подсистема передачи сообщений должны реагировать на отказы узла или коммуникационного канала во время взаимодействия? Что нужно делать, если приемник не готов принять сообщение, нужно ли отбрасывать сообщение или сохранять его в буфере? А если сохранять, то как быть, если буфер уже заполнен? Разрешено ли приемнику изменять порядок обработки сообщений в соответствии с их важностью? Ответы на подобные вопросы составляют семантику конкретного протокола передачи сообщений.
При передаче сообщений могут возникнуть ситуации, когда принимающий процесс оказывается не готовым обработать сообщение при его прибытии, но для процесса было бы желательным, чтобы операционная система на время сохранила поступившее сообщение в буфере для последующей обработки. Способ буферизации тесно связан со способом синхронизации процессов при обмене сообщениями. При использовании синхронных, то есть блокирующих примитивов, можно вообще обойтись без буферизации сообщений операционной системой. При этом возможны два варианта организации работы примитивов. В первом случае процесс-отправитель подготавливает сообщение в своей памяти и обращается к примитиву send, после чего процесс блокируется. Операционная система отправителя ждет, когда процесс-получатель выполнит на своем компьютере примитив receive, в результате чего ОС получателя направит служебное сообщение-подтверждение готовности к приему основного сообщения. После получения такого подтверждения ОС на компьютере-отправителе разблокирует процесс-отправитель и тот немедленно после этого пошлет сообщение по сети. Процесс-получатель после обращения к примитиву receive также переводится своей ОС в состояние ожидания, из которого он выходит при поступлении сообщения по сети. Сообщение немедленно копируется ОС в память процесса-получателя, не требуя буферизации, так как процесс ожидает его прихода и готов к его обработке.
Буферизация не требуется и при другом варианте обмена сообщениями, когда процесс-отправитель посылает сообщение в сеть, не дожидаясь прихода от получателя подтверждения о готовности к приему. Затем процесс-отправитель блокируется либо до прихода такого подтверждения (в этом случае никакой дополнительной работы с данным сообщением не выполняется), либо до истечения тайм-аута, после которого сообщение посылается вновь, причем в случае многократных повторных неудачных попыток сообщение отбрасывается. В обоих случаях сообщение непосредственно из памяти процесса-отправителя попадает в сеть, а после прихода из сети — в память процесса-получателя, минуя буфер, поддерживаемый системой. Однако такая организация на практике в сетевых операционных системах не применяется, так как в первом варианте процесс-получатель может достаточно долго ждать, пока сообщение будет передано по сети, а во втором — из-за неготовности процесса-получателя сообщение может многократно бесполезно передаваться по сети, засоряя каналы связи.
Именно поэтому при использовании синхронных примитивов все же предусматривают буферизацию. При этом буфер, как правило, выбирается размером в одно сообщение, так как процесс-отправитель не может послать следующее сообщение, не получив подтверждения о приеме предыдущего. Сообщение помещается в буфер, поддерживаемый операционной системой компьютера-получателя, если в момент его прихода процесс-получатель не может обработать сообщение немедленно, например из-за того, что процесс либо не является текущим, либо не готов к приему сообщения, так как не обратился к примитиву receive. Буфер может располагаться как в системной области памяти, так и в области памяти пользовательского процесса, в любом случае буфером управляет операционная система, модули которой получают сообщения по сети.
Для всех вариантов обмена сообщениями с помощью асинхронных примитивов необходима буферизация. Поскольку при асинхронном обмене процесс-отправитель может посылать сообщение всегда, когда ему это требуется, не дожидаясь подтверждения от процесса-получателя, для исключений потерь сообщений требуется буфер неограниченной длины. Так как буфер в реальной системе всегда имеет ограниченный размер, то могут возникать ситуации с переполнением буфера и на них нужно каким-то образом реагировать. Асинхронный обмен с управлением потоком - это наиболее сложный способ организации обмена сообщениями, так как для повышения эффективности, то есть максимизации скорости обмена и минимизации потерь, он требует применения сложных алгоритмов приостановки и возобновления процесс передачи, например таких, которые применяются в протоколе TCP.
Обычно операционная система предоставляет для прикладные процессов специальный примитив для создания буферов сообщений. Такого рода примитив процесс должен использовать перед тем, как отправлять или получать сообщения с помощью примитивов send и receive. При создании буфера его размер может либо устанавливаться по умолчанию, либо выбираться прикладным процессом. Часто такой буфер носит название порта (port), или почтового ящика (mailbox).